Merged from trunk 10458:10480
authorFabian Groffen <grobian@gentoo.org>
Mon, 2 Jun 2008 20:33:01 +0000 (20:33 -0000)
committerFabian Groffen <grobian@gentoo.org>
Mon, 2 Jun 2008 20:33:01 +0000 (20:33 -0000)
   | 10459   | Bug #222439 - Add doman support for foo.lang.N filenames.    |
   | zmedico | Behavior is as follows: foo.1 -> man/man1/foo.1 foo.lang.1   |
   |         | -> man/lang/man1/foo.1 Thanks to Yuri Vasilevski for the     |
   |         | initial patch. I've modified it to use bash's =~ operator    |
   |         | and corresponding $BASH_REMATCH array.                       |

   | 10460   | Convert doman to use bash's =~ operator instead of calling   |
   | zmedico | egrep.                                                       |

   | 10461   | Make set_term_size() handle the CommandNotFound exception if |
   | zmedico | stty is missing for some reason.                             |

   | 10462   | Make the new behavior from bug #222439 conditional on EAPI   |
   | zmedico | being something other than 0 or 1.                           |

   | 10465   | Bug #223447 - Adjust the "installed packages are masked"     |
   | zmedico | display to recognize packages that are masked by keywords    |
   |         | and are eligible for uninstallation by depclean. This is a   |
   |         | workaround for the fact that depclean may fail to recognize  |
   |         | that the package is eligible for uninstall due to            |
   |         | differences in visibility filtering which can lead to        |
   |         | differences in || dep evaluation. TODO: Share visibility     |
   |         | code to fix this inconsistency.                              |

   | 10467   | Update the fix from bug #220341 to protect USE deps from     |
   | zmedico | being neglected.                                             |

   | 10468   | Skip redundant Atom construction in select_package().        |
   | zmedico |                                                              |

   | 10470   | Fix the code from bug #220341 so that it matches the atom    |
   | zmedico | against the installed package, like it's supposed to.        |

   | 10471   | Filter out installed keyword masked packages when populating |
   | zmedico | RootConfig.visible_pkgs since otherwise such packages can    |
   |         | trigger undesireable results inside                          |
   |         | depgraph._iter_atoms_for_pkg().                              |

   | 10474   | Make the unmerge time CONFIG_MEMORY_FILE pruning slightly    |
   | zmedico | more aggressive, so that anything not claimed by another     |
   |         | package in the same slot is pruned.                          |

   | 10476   | Save the content of CONFIG_MEMORY_FILE in dblink.treewalk()  |
   | zmedico | _before_ unmerging other instances, in order to avoid        |
   |         | overwritting/undoing the pruning that's done during unmerge. |

   | 10478   | Make --skipfirst handle missing dependencies by dropping the |
   | zmedico | merge tasks that have missing dependencies. This involves    |
   |         | creating an entirely new depgraph each time that a missing   |
   |         | dependency is discovered. This isn't the most efficient      |
   |         | approach but it's simple and it works well. This new         |
   |         | behavior can server as a fix for bug #12768 by calling       |
   |         | emerge --resume --skipfirst each time that emerge exits      |
   |         | unsuccessfully. This was possible before, but now packages   |
   |         | with unsatisfied dependencies will be properly identified    |
   |         | and dropped from the list.                                   |

   | 10479   | Clean up exception reference from previous commit.           |
   | zmedico |                                                              |

   | 10480   | When --skipfirst drops packages due to unsatisfied           |
   | zmedico | dependencies, display a warning message with the list of     |
   |         | dropped packages.                                            |

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

bin/doman
pym/_emerge/__init__.py
pym/portage/__init__.py
pym/portage/dbapi/vartree.py
pym/portage/output.py

index 1e82032150ce401acea2726b491bd152a5869190..59de9779429b2c528b5bdb88b561fcee38284aa6 100755 (executable)
--- a/bin/doman
+++ b/bin/doman
@@ -31,15 +31,22 @@ for x in "$@" ; do
                suffix=${realname##*.}
        fi
 
-       mandir=${i18n}man${suffix:0:1}
+       if ! hasq "${EAPI:-0}" 0 1 && \
+               [[ $x =~ (.*)\.([a-z][a-z](_[A-Z][A-Z])?)\.(.*) ]] ; then
+               name=${BASH_REMATCH[1]##*/}.${BASH_REMATCH[4]}
+               mandir=${BASH_REMATCH[2]}/man${suffix:0:1}
+       else
+               name=${x##*/}
+               mandir=${i18n}man${suffix:0:1}
+       fi
 
-       if echo ${mandir} | @EGREP@ -q 'man[0-9n](|f|p|pm)$' -; then
+       if [[ ${mandir} =~ man[0-9n](|f|p|pm)$ ]] ; then
                if [[ -s ${x} ]] ; then
                        if [[ ! -d ${ED}/usr/share/man/${mandir} ]] ; then
                                install -d "${ED}/usr/share/man/${mandir}"
                        fi
 
-                       install -m0644 "${x}" "${ED}/usr/share/man/${mandir}"
+                       install -m0644 "${x}" "${ED}/usr/share/man/${mandir}/${name}"
                        ((ret+=$?))
                elif [[ ! -e ${x} ]] ; then
                        vecho "doman: ${x} does not exist" 1>&2
index 5d0bce859b95acce338faa97605cfb6f7f765cf1..c84e3beb0bb4d1c9aaf1a105c69f8a9a009653b2 100644 (file)
@@ -2017,7 +2017,7 @@ class depgraph(object):
                self._displayed_list = None
                self._pprovided_args = []
                self._missing_args = []
-               self._masked_installed = []
+               self._masked_installed = set()
                self._unsatisfied_deps_for_display = []
                self._unsatisfied_blockers_for_display = None
                self._circular_deps_for_display = None
@@ -2326,12 +2326,6 @@ class depgraph(object):
                                        del e
                                        return 0
 
-               if pkg.installed:
-                       # Warn if an installed package is masked and it
-                       # is pulled into the graph.
-                       if not visible(pkgsettings, pkg):
-                               self._masked_installed.append((pkg, pkgsettings))
-
                if args:
                        self._set_nodes.add(pkg)
 
@@ -3061,7 +3055,8 @@ class depgraph(object):
                # List of acceptable packages, ordered by type preference.
                matched_packages = []
                highest_version = None
-               atom = portage.dep.Atom(atom)
+               if not isinstance(atom, portage.dep.Atom):
+                       atom = portage.dep.Atom(atom)
                atom_cp = atom.cp
                existing_node = None
                myeb = None
@@ -3104,12 +3099,31 @@ class depgraph(object):
                                # the newly built package still won't have the expected slot.
                                # Therefore, assume that such SLOT dependencies are already
                                # satisfied rather than forcing a rebuild.
-                               if installed and not cpv_list and matched_packages \
-                                       and portage.dep.dep_getslot(atom):
+                               if installed and not cpv_list and \
+                                       matched_packages and atom.slot:
                                        for pkg in matched_packages:
-                                               if vardb.cpv_exists(pkg.cpv):
-                                                       cpv_list = [pkg.cpv]
-                                                       break
+                                               if not vardb.cpv_exists(pkg.cpv):
+                                                       continue
+                                               inst_pkg = self._pkg_cache.get(
+                                                       (pkg_type, root, pkg.cpv, "nomerge"))
+                                               if inst_pkg is None:
+                                                       metadata = izip(self._mydbapi_keys,
+                                                               vardb.aux_get(cpv, self._mydbapi_keys))
+                                                       inst_pkg = Package(built=built, cpv=pkg.cpv,
+                                                               installed=installed, metadata=metadata,
+                                                               onlydeps=onlydeps, root_config=root_config,
+                                                               type_name=pkg_type)
+                                                       self._pkg_cache[inst_pkg] = inst_pkg
+                                               # Remove the slot from the atom and verify that
+                                               # the package matches the resulting atom.
+                                               atom_without_slot = portage.dep.remove_slot(atom)
+                                               if atom.use:
+                                                       atom_without_slot += str(atom.use)
+                                               atom_without_slot = portage.dep.Atom(atom_without_slot)
+                                               if portage.match_from_list(
+                                                       atom_without_slot, [inst_pkg]):
+                                                       cpv_list = [inst_pkg.cpv]
+                                               break
 
                                if not cpv_list:
                                        continue
@@ -3455,11 +3469,51 @@ class depgraph(object):
                                portdb = self.trees[myroot]["porttree"].dbapi
                                pkgsettings = self.pkgsettings[myroot]
                                final_db = self.mydbapi[myroot]
+
+                               graph_complete_for_root = "complete" in self.myparams or \
+                                       (myroot == self.target_root and \
+                                       ("deep" in self.myparams or "empty" in self.myparams) and \
+                                       not self._required_set_names.difference(self._sets))
+
                                blocker_cache = BlockerCache(myroot, vardb)
                                stale_cache = set(blocker_cache)
                                for pkg in vardb:
                                        cpv = pkg.cpv
                                        stale_cache.discard(cpv)
+
+                                       # Check for masked installed packages. For keyword
+                                       # mask there are a couple of common cases that are
+                                       # likely to generate unwanted noise:
+                                       #
+                                       #  * Packages missing /var/db/pkg/*/*/KEYWORDS entries
+                                       #    due to having been installed by an old version of
+                                       #    portage.
+                                       #
+                                       #  * Packages installed by overriding ACCEPT_KEYWORDS
+                                       #    via the environment.
+                                       #
+                                       # To avoid unwanted noise, only warn about keyword
+                                       # masks if all of the following are true:
+                                       #
+                                       #  * KEYWORDS is not empty (not installed by old portage).
+                                       #
+                                       #  * The graph is complete and the package has not been
+                                       #    pulled into the dependency graph. It's eligible for
+                                       #    depclean, but depclean may fail to recognize it as
+                                       #    such due to differences in visibility filtering which
+                                       #    can lead to differences in || dep evaluation.
+                                       #    TODO: Share visibility code to fix this inconsistency.
+
+                                       if pkg in final_db:
+                                               if not visible(pkgsettings, pkg):
+                                                       self._masked_installed.add(pkg)
+                                               elif graph_complete_for_root and \
+                                                       pkgsettings.getMissingKeywords(
+                                                       pkg.cpv, pkg.metadata) and \
+                                                       pkg.metadata["KEYWORDS"].split() and \
+                                                       not self.digraph.contains(pkg):
+                                                       self._masked_installed.add(pkg)
+
                                        blocker_atoms = None
                                        blockers = None
                                        if self.digraph.contains(pkg):
@@ -5124,8 +5178,9 @@ class depgraph(object):
                        sys.stderr.write("".join(msg))
 
                masked_packages = []
-               for pkg, pkgsettings in self._masked_installed:
-                       root_config = self.roots[pkg.root]
+               for pkg in self._masked_installed:
+                       root_config = pkg.root_config
+                       pkgsettings = root_config.settings
                        mreasons = get_masking_status(pkg, pkgsettings, root_config)
                        masked_packages.append((root_config, pkgsettings,
                                pkg.cpv, pkg.metadata, mreasons))
@@ -5254,13 +5309,6 @@ class depgraph(object):
                if not isinstance(mergelist, list):
                        mergelist = []
 
-               if mergelist and "--skipfirst" in self.myopts:
-                       for i, task in enumerate(mergelist):
-                               if isinstance(task, list) and \
-                                       task and task[-1] == "merge":
-                                       del mergelist[i]
-                                       break
-
                fakedb = self.mydbapi
                trees = self.trees
                serialized_tasks = []
@@ -5577,7 +5625,9 @@ class depgraph(object):
                def __setitem__(self, k, v):
                        dict.__setitem__(self, k, v)
                        root_config = self._depgraph.roots[v.root]
-                       if visible(root_config.settings, v):
+                       if visible(root_config.settings, v) and \
+                               not (v.installed and \
+                               v.root_config.settings.getMissingKeywords(v.cpv, v.metadata)):
                                root_config.visible_pkgs.cpv_inject(v)
 
 class RepoDisplay(object):
@@ -8393,11 +8443,43 @@ def action_build(settings, trees, mtimedb,
                if show_spinner:
                        print "Calculating dependencies  ",
                myparams = create_depgraph_params(myopts, myaction)
-               mydepgraph = depgraph(settings, trees,
-                       myopts, myparams, spinner)
+
+               resume_data = mtimedb["resume"]
+               mergelist = resume_data["mergelist"]
+               if mergelist and "--skipfirst" in myopts:
+                       for i, task in enumerate(mergelist):
+                               if isinstance(task, list) and \
+                                       task and task[-1] == "merge":
+                                       del mergelist[i]
+                                       break
+
+               dropped_tasks = set()
+
                success = False
                try:
-                       success = mydepgraph.loadResumeCommand(mtimedb["resume"])
+                       while True:
+                               mydepgraph = depgraph(settings, trees,
+                                       myopts, myparams, spinner)
+                               try:
+                                       success = mydepgraph.loadResumeCommand(mtimedb["resume"])
+                               except depgraph.UnsatisfiedResumeDep, e:
+                                       if "--skipfirst" not in myopts:
+                                               raise
+                                       unsatisfied_parents = set(dep.parent for dep in e.value)
+                                       pruned_mergelist = []
+                                       for task in mergelist:
+                                               if isinstance(task, list) and \
+                                                       tuple(task) in unsatisfied_parents:
+                                                       continue
+                                               pruned_mergelist.append(task)
+                                       if not pruned_mergelist:
+                                               raise
+                                       mergelist[:] = pruned_mergelist
+                                       dropped_tasks.update(unsatisfied_parents)
+                                       del e
+                                       continue
+                               else:
+                                       break
                except (portage.exception.PackageNotFound,
                        mydepgraph.UnsatisfiedResumeDep), e:
                        if show_spinner:
@@ -8431,7 +8513,9 @@ def action_build(settings, trees, mtimedb,
                                msg = "The resume list contains packages " + \
                                        "with dependencies that have not been " + \
                                        "installed yet. Please restart/continue " + \
-                                       "the operation manually."
+                                       "the operation manually, or use --skipfirst " + \
+                                       "to skip the first package in the list and " + \
+                                       "any other packages that may have missing dependencies."
                                for line in wrap(msg, 72):
                                        out.eerror(line)
                        elif isinstance(e, portage.exception.PackageNotFound):
@@ -8448,7 +8532,15 @@ def action_build(settings, trees, mtimedb,
                        if show_spinner:
                                print "\b\b... done!"
 
-               if not success:
+               if success:
+                       if dropped_tasks:
+                               portage.writemsg("!!! One or more packages have been " + \
+                                       "dropped due to unsatisfied dependencies:\n\n",
+                                       noiselevel=-1)
+                               for task in dropped_tasks:
+                                       portage.writemsg("  " + str(task) + "\n", noiselevel=-1)
+                               portage.writemsg("\n", noiselevel=-1)
+               else:
                        mydepgraph.display_problems()
                        if not (ask or pretend):
                                # delete the current list and also the backup
index 1c68e38dad1ade5b404ae1e1b91840d5fbe271c7..e4b493baaf733ca24d007f4682a2d91f469787ee 100644 (file)
@@ -6228,11 +6228,6 @@ def getmaskingstatus(mycpv, settings=None, portdb=None):
                                kmask="~"+myarch
                                break
 
-       # Assume that the user doesn't want to be bothered about
-       # KEYWORDS of packages that are already installed.
-       if kmask and not installed:
-               rValue.append(kmask+" keyword")
-
        try:
                missing_licenses = settings.getMissingLicenses(mycpv, metadata)
                if missing_licenses:
@@ -6247,6 +6242,11 @@ def getmaskingstatus(mycpv, settings=None, portdb=None):
        except portage.exception.InvalidDependString, e:
                rValue.append("LICENSE: "+str(e))
 
+       # Only show KEYWORDS masks for installed packages
+       # if they're not masked for any other reason.
+       if kmask and (not installed or not rValue):
+               rValue.append(kmask+" keyword")
+
        return rValue
 
 
index 9539e46b46b0353e433734e93f53dde863c3569f..1cba9a55b4b362d3e23fc9326007d022d50abbc8 100644 (file)
@@ -1620,9 +1620,6 @@ class dblink(object):
                                                continue
                                if obj.startswith(dest_root):
                                        relative_path = obj[dest_root_len:]
-                                       if not others_in_slot and \
-                                               relative_path in cfgfiledict:
-                                               stale_confmem.append(relative_path)
                                        is_owned = False
                                        for dblnk in others_in_slot:
                                                if dblnk.isowner(relative_path, dest_root):
@@ -1633,6 +1630,8 @@ class dblink(object):
                                                # don't unmerge it.
                                                show_unmerge("---", "replaced", file_type, obj)
                                                continue
+                                       elif relative_path in cfgfiledict:
+                                               stale_confmem.append(relative_path)
                                # next line includes a tweak to protect modules from being unmerged,
                                # but we don't protect modules from being overwritten if they are
                                # upgraded. We effectively only want one half of the config protection
@@ -2430,6 +2429,12 @@ class dblink(object):
                outfile.flush()
                outfile.close()
 
+               # write out our collection of md5sums
+               cfgfiledict.pop("IGNORE", None)
+               ensure_dirs(os.path.dirname(conf_mem_file),
+                       gid=portage_gid, mode=02750, mask=02)
+               writedict(cfgfiledict, conf_mem_file)
+
                # These caches are populated during collision-protect and the data
                # they contain is now invalid. It's very important to invalidate
                # the contents_inodes cache so that FEATURES=unmerge-orphans
@@ -2517,16 +2522,6 @@ class dblink(object):
                self.vartree.dbapi.cpcache.pop(self.mysplit[0], None)
                contents = self.getcontents()
 
-               #write out our collection of md5sums
-               if cfgfiledict.has_key("IGNORE"):
-                       del cfgfiledict["IGNORE"]
-
-               my_private_path = os.path.join(destroot, PRIVATE_PATH)
-               ensure_dirs(my_private_path, gid=portage_gid, mode=02750, mask=02)
-
-               writedict(cfgfiledict, conf_mem_file)
-               del conf_mem_file
-
                # regenerate reverse NEEDED map
                self.vartree.dbapi.linkmap.rebuild()
 
index e9a1a559a73c8233a41db6fed9217a4b0af92c8c..a7a500cd4f120efcf208cc8a87a85c79819c83a9 100644 (file)
@@ -13,7 +13,8 @@ import shlex
 import sys
 from portage.const import COLOR_MAP_FILE
 from portage.util import writemsg
-from portage.exception import PortageException, ParseError, PermissionDenied, FileNotFound
+from portage.exception import CommandNotFound, FileNotFound, \
+       ParseError, PermissionDenied, PortageException
 
 havecolor=1
 dotitles=1
@@ -405,7 +406,10 @@ def set_term_size(lines, columns, fd):
        """
        from portage.process import spawn
        cmd = ["stty", "rows", str(lines), "columns", str(columns)]
-       spawn(cmd, env=os.environ, fd_pipes={0:fd})
+       try:
+               spawn(cmd, env=os.environ, fd_pipes={0:fd})
+       except CommandNotFound:
+               writemsg("portage: stty: command not found\n", noiselevel=-1)
 
 class EOutput(object):
        """