Merged from trunk 9468:9491
authorFabian Groffen <grobian@gentoo.org>
Thu, 20 Mar 2008 20:32:23 +0000 (20:32 -0000)
committerFabian Groffen <grobian@gentoo.org>
Thu, 20 Mar 2008 20:32:23 +0000 (20:32 -0000)
   | 9469    | add a placeholder for repository paths in 'filename' and     |
   | genone  | 'directory' options for StaticFileSet                        |

   | 9470    | avoid importing portage to improve performance               |
   | genone  |                                                              |

   | 9471    | When in strict mode, make paren_reduce() detect missing      |
   | zmedico | whitespace around parenthesis tokens and raise an            |
   |         | InvalidDependString exception. Strict mode is not enabled    |
   |         | when handling installed packages, so this will only affect   |
   |         | repoman and packages that haven't been installed yet. Thanks |
   |         | to zlin for reporting.                                       |

   | 9472    | Bug #213540 - Make sure DESTTREE=/usr is set before inherit  |
   | zmedico | calls.                                                       |

   | 9473    | Bug #213592 - Fix --onlydeps breakage.                       |
   | zmedico |                                                              |

   | 9474    | Bug #212882 - For compatibility with ENOENT exceptions       |
   | zmedico | raised from fstat calls with CIFS, wrap fstat calls with an  |
   |         | appropriate exception handler.                               |

   | 9475    | Bug #174634 - Fix ebuild 'digest' and 'manifest' behave 100% |
   | zmedico | identically and update the docs to reflect this.             |

   | 9476    | Bug #149745 - Generate an eqawarn message if dohtml is       |
   | zmedico | called on a directory without the recursive option. Thanks   |
   |         | to brad walker <bradmwalker@cableone.net> for reporting.     |

   | 9477    | use a dict with CP keys in unmerge() to avoid double         |
   | genone  | unmerges if a package is matched by multiple atoms           |

   | 9478    | fix artifact from r9477                                      |
   | genone  |                                                              |

   | 9479    | add @ to the list of invalid atom chars so sets with prefix  |
   | genone  | aren't considered as atoms accidentally                      |

   | 9480    | add unmerge protection for packages still referenced by      |
   | genone  | installed package sets                                       |

   | 9481    | make filesets non-greedy by default                          |
   | genone  |                                                              |

   | 9482    | Bug #139023 - When the --digest option is enabled, advise    |
   | zmedico | the user that `repoman manifest` is the preferred way to     |
   |         | generate manifests.                                          |

   | 9483    | Bug #201045 - Fix unmerge() to preserve unmerge order so     |
   | zmedico | that dependencies are respected. Also, eliminate duplicate   |
   |         | package selections since the same package may be selected by |
   |         | multiple atoms but we can only unmerge it once.              |

   | 9484    | Only show the eqawarn from bug #149745 when nothing is found |
   | zmedico | to install. Also, exit successfully unless nothing is found  |
   |         | to install.                                                  |

   | 9485    | Bug #210575 - Optimize doins -r so that it doesn't call      |
   | zmedico | itself recursively, since it's faster to handle the          |
   |         | recursion internally. Thanks to Benedikt Böhm               |
   |         | <hollow@gentoo.org> for the initial patch.                   |

   | 9486    | Bug #210690 - Fix --help output to show the leading slash on |
   | zmedico | the path to the world file.                                  |

   | 9487    | Strip trailing slashes from directory paths if necessary.    |
   | zmedico |                                                              |

   | 9488    | display versions of other slots for new-slot installs (bug   |
   | genone  | #213790)                                                     |

   | 9489    | Fix quoting.                                                 |
   | zmedico |                                                              |

   | 9491    | readd digestentry.unused category as it's still relevant     |
   | genone  |                                                              |

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

12 files changed:
bin/dohtml
bin/doins
bin/ebuild
bin/ebuild.sh
bin/pkgname
bin/regenworld
bin/repoman
man/ebuild.1
pym/_emerge/__init__.py
pym/portage/dep.py
pym/portage/locks.py
pym/portage/sets/files.py

index def135d67978f0b65a50e90320037c49796f2751..d65a31a15c4dcf9935b06a5c65cb59769effb3e8 100755 (executable)
@@ -39,6 +39,15 @@ def dodir(path):
 def dofile(src,dst):
        os.spawnlp(os.P_WAIT, "install", "install", "-m0644", src, dst)
 
+def eqawarn(lines):
+       cmd = "source '%s/isolated-functions.sh' ; " % \
+               os.environ["PORTAGE_BIN_PATH"]
+       for line in lines:
+               cmd += "eqawarn \"%s\" ; " % line
+       os.spawnlp(os.P_WAIT, "bash", "bash", "-c", cmd)
+
+skipped_directories = []
+
 def install(basename, dirname, options, prefix=""):
        fullpath = basename
        if prefix:
@@ -62,6 +71,10 @@ def install(basename, dirname, options, prefix=""):
                        pfx = basename
                        if prefix: pfx = prefix + "/" + pfx
                        install(i, dirname, options, pfx)
+       elif not options.recurse and os.path.isdir(fullpath):
+               global skipped_directories
+               skipped_directories.append(fullpath)
+               return False
        else:
                return False
        return True
@@ -155,19 +168,22 @@ def main():
                print "Document prefix : '" + options.doc_prefix         + "'"
                print "Allowed files :", options.allowed_files
 
-       success = True
+       success = False
        
        for x in args:
                basename = os.path.basename(x)
                dirname  = os.path.dirname(x)
-               if not install(basename, dirname, options):
-                       success = False
-       
+               success |= install(basename, dirname, options)
+
        if success:
                retcode = 0
        else:
                retcode = 1
-       
+               global skipped_directories
+               for x in skipped_directories:
+                       eqawarn(["QA Notice: dohtml on directory " + \
+                               "'%s' without recursion option" % x])
+
        sys.exit(retcode)
 
 if __name__ == "__main__":
index 16a1b85a1424459adf85c620b5c8274e8c885924..26496a8bb19e2abdae00a54d9677bd73ea159d12 100755 (executable)
--- a/bin/doins
+++ b/bin/doins
@@ -16,11 +16,6 @@ if [[ "$1" == "-r" ]] ; then
 else
        DOINSRECUR=n
 fi
-[[ -z ${INSDEPTH} ]] && declare -i INSDEPTH=0
-if [[ ${INSDEPTH} -gt 30 ]] ; then
-       echo "${0##*/}: sanity check ... 30 directories is too much :(" 2>&1
-       exit 1
-fi
 
 if [[ ${INSDESTTREE#${D}} != "${INSDESTTREE}" ]]; then
        vecho "-------------------------------------------------------" 1>&2
@@ -40,24 +35,41 @@ fi
 
 [[ ! -d ${ED}${INSDESTTREE} ]] && dodir "${INSDESTTREE}"
 
+_doins() {
+       local mysrc="$1" mydir="$2"
+
+       if [ -L "$mysrc" ] ; then
+               cp "$mysrc" "${T}"
+               mysrc="${T}/${mysrc##*/}"
+       fi
+
+       install ${INSOPTIONS} "${mysrc}" "${ED}${INSDESTTREE}/${mydir}"
+}
+
+_xdoins() {
+       while read -d $'\0' x ; do
+               _doins "$x" "${x%/*}"
+       done
+}
+
 for x in "$@" ; do
-       if [ -L "$x" ] ; then
-               cp "$x" "${T}"
-               mysrc="${T}/$(basename "${x}")"
-       elif [ -d "$x" ] ; then
+       if [ -d "$x" ] ; then
                if [ "${DOINSRECUR}" == "n" ] ; then
                        continue
                fi
 
-               mydir="${INSDESTTREE}/$(basename "${x}")"
-               find "${x}" -mindepth 1 -maxdepth 1 -exec \
-                       env \
-                               INSDESTTREE="${mydir}" \
-                               INSDEPTH=$((INSDEPTH+1)) \
-                               doins -r {} \;
-               continue
+               while [ "$x" != "${x%/}" ] ; do
+                       x=${x%/}
+               done
+               if [ "$x" = "${x%/*}" ] ; then
+                       pushd "$PWD" >/dev/null
+               else
+                       pushd "${x%/*}" >/dev/null
+               fi
+               find "${x##*/}" -type d -exec dodir "${INSDESTTREE}/{}" \;
+               find "${x##*/}" \( -type f -or -type l \) -print0 | _xdoins
+               popd >/dev/null
        else
-               mysrc="${x}"
+               _doins "${x}"
        fi
-       install ${INSOPTIONS} "${mysrc}" "${ED}${INSDESTTREE}"
 done
index e106fdcbcf7566fc2e351c1d80d86a5dadd62884..53fa7cdd4e466f122bf6b815f3464fff4510cc87 100755 (executable)
@@ -179,7 +179,7 @@ for arg in pargs:
                        stale_env_warning()
                        checked_for_stale_env = True
 
-               if arg == "digest" and force:
+               if arg in ("digest", "manifest") and force:
                        discard_digests(ebuild, tmpsettings, portage.portdb)
                a = portage.doebuild(ebuild, arg, portage.root, tmpsettings,
                        debug=debug, tree=mytree)
index 1e5a4e53923a4bee082d2a8a507f98176e6d6e43..471743ce0911ec5acb54cce3083e13808b37dfbe 100755 (executable)
@@ -263,6 +263,10 @@ register_die_hook() {
 #if no perms are specified, dirs/files will have decent defaults
 #(not secretive, but not stupid)
 umask 022
+export DESTTREE=/usr
+export INSDESTTREE=""
+export _E_EXEDESTTREE_=""
+export _E_DOCDESTTREE_=""
 export INSOPTIONS="-m0644"
 export EXEOPTIONS="-m0755"
 export LIBOPTIONS="-m0644"
@@ -1060,7 +1064,7 @@ dyn_help() {
        echo "  help        : show this help screen"
        echo "  setup       : execute package specific setup actions"
        echo "  fetch       : download source archive(s) and patches"
-       echo "  digest      : create a digest and a manifest file for the package"
+       echo "  digest      : create a manifest file for the package"
        echo "  manifest    : create a manifest file for the package"
        echo "  unpack      : unpack/patch sources (auto-fetch if needed)"
        echo "  compile     : compile sources (auto-fetch/unpack if needed)"
index 41ae9f13d71a20af5475362bb3cea5d0ecb9a61c..e50b35352a606baeeddba2ce35b963e82cc064a6 100755 (executable)
@@ -3,8 +3,6 @@
 # Distributed under the terms of the GNU General Public License v2
 # $Id$
 
-import sys, os
-
 # for an explanation on this logic, see pym/_emerge/__init__.py
 import os
 import sys
@@ -13,8 +11,9 @@ if os.environ.__contains__("PORTAGE_PYTHONPATH"):
 else:
        sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "pym"))
 import portage
+from portage.versions import pkgsplit
 
-a=portage.pkgsplit(sys.argv[1])
+a=pkgsplit(sys.argv[1])
 if a:
        print a[0],a[1],a[2][1:]
        sys.exit(0)
index 369d7faba6c5bb280ccfbc16a44e02b6840ffc02..fe88a24103ba72b4f05852d33b2b7f602132ffbb 100755 (executable)
@@ -44,12 +44,14 @@ def isunwanted(pkgline):
                __uniqlist__.append(pkgline)
                return True
 
+world_file = os.path.join("/", portage.WORLD_FILE)
+
 # show a little description if we have arguments
 if len(sys.argv) >= 2 and sys.argv[1] in ["-h", "--help"]:
        print "This script regenerates the portage world file by checking the portage"
        print "logfile for all actions that you've done in the past. It ignores any"
        print "arguments except --help. It is recommended that you make a backup of"
-       print "your existing world file (%s) before using this tool." % portage.WORLD_FILE
+       print "your existing world file (%s) before using this tool." % world_file
        sys.exit(0)
 
 worldlist = portage.grabfile(os.path.join("/", portage.WORLD_FILE))
index ae0a5ca32e5921d33c7da486309460e058a7a762..da0cfc4f7ec4d715675be5bb6b483374c8a458bf 100755 (executable)
@@ -282,6 +282,7 @@ qahelp={
        "LICENSE.invalid":"This ebuild is listing a license that doesnt exist in portages license/ dir.",
        "KEYWORDS.invalid":"This ebuild contains KEYWORDS that are not listed in profiles/arch.list or for which no valid profile was found",
        "RESTRICT.invalid":"This ebuild contains invalid RESTRICT values.",
+       "digestentry.unused":"Some files listed in the Manifest aren't referenced in SRC_URI",
        "ebuild.nostable":"There are no ebuilds that are marked as stable for your ARCH",
        "ebuild.allmasked":"All ebuilds are masked for this package (Package level only)",
        "ebuild.majorsyn":"This ebuild has a major syntax error that may cause the ebuild to fail partially or fully",
@@ -301,6 +302,7 @@ qacats.sort()
 qawarnings=[
 "changelog.missing",
 "changelog.notadded",
+"digestentry.unused",
 "ebuild.notadded",
 "ebuild.nostable",
 "ebuild.allmasked",
index e7ebabfe090bd826a3f1a052e609a6014c1b86bf..4e74a2783fb13b2cd7b8fda91a0a222f5a348a1c 100644 (file)
@@ -64,18 +64,17 @@ in files/digest\-[package]\-[version\-rev], a warning is printed
 and ebuild exits with an error code of 1.
 .TP
 .BR digest
-Creates a digest file for the package in
-/usr/portage/[category]/[package]/files/.  This digest file lists the
-checksums of all the files found in the SRC_URI. See the \fB\-\-force\fR
-option if you would like to prevent digests from being assumed. For further
-information regarding the behavior of this command, see the documentation for
-the \fIassume\-digests\fR value of the \fBFEATURES\fR variable in
-\fBmake.conf\fR(5).
+This is now equivalent to the \fImanifest\fR command.
 .TP
 .BR manifest
-Updates the manifest file for the package.  This performs the second half
-of the digest command, creating md5sums for the files in the files
-directory as well as the ebuild.
+Updates the manifest file for the package.  This creates checksums for all
+of the files found in the same directory as the current ebuild as well as
+the recursive contents of the files subdirectory. It also creates checksums
+for all of the files listed in SRC_URI for each ebuild. For further
+information regarding the behavior of this command, see the documentation for
+the \fIassume\-digests\fR value of the \fBFEATURES\fR variable in
+\fBmake.conf\fR(5). See the \fB\-\-force\fR option if you would like to
+prevent digests from being assumed. 
 .TP
 .BR unpack
 Extracts the sources to a subdirectory in the \fIbuild directory\fR
@@ -173,7 +172,8 @@ information is not incorporated into the RPM.
 .SH OPTIONS
 .TP
 \fB--force\fR
-When used together with the digest command, this option forces regeneration of
+When used together with the digest or manifest command,
+this option forces regeneration of
 digests for all distfiles associated with the current ebuild. Any distfiles
 that do not already exist in ${DISTDIR} will be automatically fetched.
 .SH "REPORTING BUGS"
index 1bf636090c0175ef2292e8ad96b219bcdc3bca64..8d2178195364bbbcf197e56a981dc2e773dbaa8c 100644 (file)
@@ -1271,7 +1271,7 @@ class SetArg(DependencyArg):
 
 class Dependency(object):
        __slots__ = ("__weakref__", "atom", "blocker", "depth",
-               "parent", "priority", "root")
+               "parent", "onlydeps", "priority", "root")
        def __init__(self, **kwargs):
                for myattr in self.__slots__:
                        if myattr == "__weakref__":
@@ -1694,7 +1694,8 @@ class depgraph(object):
                                                ("blocks", dep.parent.root, dep.atom), set()).add(
                                                        dep.parent)
                                continue
-                       dep_pkg, existing_node = self._select_package(dep.root, dep.atom)
+                       dep_pkg, existing_node = self._select_package(dep.root, dep.atom,
+                               onlydeps=dep.onlydeps)
                        if not dep_pkg:
                                if allow_unsatisfied:
                                        self._unsatisfied_deps.append(dep)
@@ -2324,7 +2325,7 @@ class depgraph(object):
                                                return 0, myfavorites
 
                                        self._dep_stack.append(
-                                               Dependency(atom=atom, root=myroot, parent=arg))
+                                               Dependency(atom=atom, onlydeps=onlydeps, root=myroot, parent=arg))
                                        if not self._create_graph():
                                                if isinstance(arg, SetArg):
                                                        sys.stderr.write(("\n\n!!! Problem resolving " + \
@@ -3716,7 +3717,8 @@ class depgraph(object):
 
                                #we need to use "--emptrytree" testing here rather than "empty" param testing because "empty"
                                #param is used for -u, where you still *do* want to see when something is being upgraded.
-                               myoldbest=""
+                               myoldbest = ""
+                               myinslotlist = None
                                installed_versions = vardb.match(portage.cpv_getkey(pkg_key))
                                if vardb.cpv_exists(pkg_key):
                                        addl="  "+yellow("R")+fetch+"  "
@@ -3738,21 +3740,22 @@ class depgraph(object):
                                                portage.cpv_getkey(pkg_key):
                                                myinslotlist = None
                                        if myinslotlist:
-                                               myoldbest=portage.best(myinslotlist)
-                                               addl="   "+fetch
+                                               myoldbest = portage.best(myinslotlist)
+                                               addl = "   " + fetch
                                                if portage.pkgcmp(portage.pkgsplit(x[2]), portage.pkgsplit(myoldbest)) < 0:
                                                        # Downgrade in slot
-                                                       addl+=turquoise("U")+blue("D")
+                                                       addl += turquoise("U")+blue("D")
                                                        if ordered:
                                                                counters.downgrades += 1
                                                else:
                                                        # Update in slot
-                                                       addl+=turquoise("U")+" "
+                                                       addl += turquoise("U") + " "
                                                        if ordered:
                                                                counters.upgrades += 1
                                        else:
                                                # New slot, mark it new.
-                                               addl=" "+green("NS")+fetch+"  "
+                                               addl = " " + green("NS") + fetch + "  "
+                                               myoldbest = vardb.match(portage.cpv_getkey(pkg_key))
                                                if ordered:
                                                        counters.newslot += 1
 
@@ -3765,11 +3768,11 @@ class depgraph(object):
                                                                portdb.findname(pkg_key),
                                                                inst_matches[0], pkg_key))
                                else:
-                                       addl=" "+green("N")+" "+fetch+"  "
+                                       addl = " " + green("N") + " " + fetch + "  "
                                        if ordered:
                                                counters.new += 1
 
-                               verboseadd=""
+                               verboseadd = ""
                                
                                if True:
                                        # USE flag display
@@ -3786,7 +3789,7 @@ class depgraph(object):
                                        cur_use = pkg_use
                                        cur_use = [flag for flag in cur_use if flag in cur_iuse]
 
-                                       if myoldbest:
+                                       if myoldbest and myinslotlist:
                                                pkg = myoldbest
                                        else:
                                                pkg = x[2]
@@ -3928,10 +3931,10 @@ class depgraph(object):
                                                verboseadd += teal("[%s]" % repoadd)
 
                                xs = list(portage.pkgsplit(x[2]))
-                               if xs[2]=="r0":
-                                       xs[2]=""
+                               if xs[2] == "r0":
+                                       xs[2] = ""
                                else:
-                                       xs[2]="-"+xs[2]
+                                       xs[2] = "-" + xs[2]
 
                                mywidth = 130
                                if "COLUMNWIDTH" in self.settings:
@@ -3943,16 +3946,22 @@ class depgraph(object):
                                                        "!!! Unable to parse COLUMNWIDTH='%s'\n" % \
                                                        self.settings["COLUMNWIDTH"], noiselevel=-1)
                                                del e
-                               oldlp=mywidth-30
-                               newlp=oldlp-30
+                               oldlp = mywidth - 30
+                               newlp = oldlp - 30
 
                                indent = " " * depth
 
                                if myoldbest:
-                                       myoldbest=portage.pkgsplit(myoldbest)[1]+"-"+portage.pkgsplit(myoldbest)[2]
-                                       if myoldbest[-3:]=="-r0":
-                                               myoldbest=myoldbest[:-3]
-                                       myoldbest=blue("["+myoldbest+"]")
+                                       if myinslotlist:
+                                               myoldbest = [myoldbest]
+                                       for key in myoldbest:
+                                               pos = myoldbest.index(key)
+                                               key = portage.pkgsplit(key)[1] + "-" + portage.pkgsplit(key)[2]
+                                               if key[-3:] == "-r0":
+                                                       key = key[:-3]
+                                               myoldbest[pos] = key
+                                       myoldbest = blue("["+", ".join(myoldbest)+"]")
+                                       
 
                                pkg_cp = xs[0]
                                root_config = self.roots[myroot]
@@ -4940,7 +4949,7 @@ def unmerge(root_config, myopts, unmerge_action,
                mysettings = portage.config(clone=settings)
        
                if not unmerge_files:
-                       if "unmerge"==unmerge_action:
+                       if unmerge_action == "unmerge":
                                print
                                print bold("emerge unmerge") + " can only be used with specific package names"
                                print
@@ -5024,9 +5033,13 @@ def unmerge(root_config, myopts, unmerge_action,
                        not ("--quiet" in myopts):
                        print darkgreen(newline+\
                                ">>> These are the packages that would be unmerged:")
-       
+
+               # Preservation of order is required for --depclean and --prune so
+               # that dependencies are respected. Use all_selected to eliminate
+               # duplicate packages since the same package may be selected by
+               # multiple atoms.
                pkgmap = []
-               numselected=0
+               all_selected = set()
                for x in candidate_catpkgs:
                        # cycle through all our candidate deps and determine
                        # what will and will not get unmerged
@@ -5051,13 +5064,15 @@ def unmerge(root_config, myopts, unmerge_action,
                                portage.writemsg("\n--- Couldn't find '%s' to %s.\n" % \
                                        (x, unmerge_action), noiselevel=-1)
                                continue
-                       pkgmap.append({"protected":[], "selected":[], "omitted":[] })
+
+                       pkgmap.append(
+                               {"protected": set(), "selected": set(), "omitted": set()})
                        mykey = len(pkgmap) - 1
                        if unmerge_action=="unmerge":
                                        for y in mymatch:
-                                               if y not in pkgmap[mykey]["selected"]:
-                                                       pkgmap[mykey]["selected"].append(y)
-                                                       numselected=numselected+len(mymatch)
+                                               if y not in all_selected:
+                                                       pkgmap[mykey]["selected"].add(y)
+                                                       all_selected.add(y)
                        elif unmerge_action == "prune":
                                if len(mymatch) == 1:
                                        continue
@@ -5078,38 +5093,41 @@ def unmerge(root_config, myopts, unmerge_action,
                                                best_version = mypkg
                                                best_slot = myslot
                                                best_counter = mycounter
-                               pkgmap[mykey]["protected"].append(best_version)
-                               pkgmap[mykey]["selected"] = [mypkg for mypkg in mymatch \
-                                       if mypkg != best_version]
-                               numselected = numselected + len(pkgmap[mykey]["selected"])
+                               pkgmap[mykey]["protected"].add(best_version)
+                               pkgmap[mykey]["selected"].update(mypkg for mypkg in mymatch \
+                                       if mypkg != best_version and mypkg not in all_selected)
+                               all_selected.update(pkgmap[mykey]["selected"])
                        else:
                                # unmerge_action == "clean"
                                slotmap={}
                                for mypkg in mymatch:
-                                       if unmerge_action=="clean":
-                                               myslot=localtree.getslot(mypkg)
+                                       if unmerge_action == "clean":
+                                               myslot = localtree.getslot(mypkg)
                                        else:
                                                # since we're pruning, we don't care about slots
                                                # and put all the pkgs in together
-                                               myslot=0
+                                               myslot = 0
                                        if not slotmap.has_key(myslot):
-                                               slotmap[myslot]={}
-                                       slotmap[myslot][localtree.dbapi.cpv_counter(mypkg)]=mypkg
+                                               slotmap[myslot] = {}
+                                       slotmap[myslot][localtree.dbapi.cpv_counter(mypkg)] = mypkg
+                               
                                for myslot in slotmap:
-                                       counterkeys=slotmap[myslot].keys()
-                                       counterkeys.sort()
+                                       counterkeys = slotmap[myslot].keys()
                                        if not counterkeys:
                                                continue
                                        counterkeys.sort()
-                                       pkgmap[mykey]["protected"].append(
+                                       pkgmap[mykey]["protected"].add(
                                                slotmap[myslot][counterkeys[-1]])
                                        del counterkeys[-1]
                                        #be pretty and get them in order of merge:
                                        for ckey in counterkeys:
-                                               pkgmap[mykey]["selected"].append(slotmap[myslot][ckey])
-                                               numselected=numselected+1
+                                               mypkg = slotmap[myslot][ckey]
+                                               if mypkg not in all_selected:
+                                                       pkgmap[mykey]["selected"].add(mypkg)
+                                                       all_selected.add(mypkg)
                                        # ok, now the last-merged package
                                        # is protected, and the rest are selected
+               numselected = len(all_selected)
                if global_unmerge and not numselected:
                        portage.writemsg_stdout("\n>>> No outdated packages were found on your system.\n")
                        return 0
@@ -5122,9 +5140,55 @@ def unmerge(root_config, myopts, unmerge_action,
        finally:
                if vdb_lock:
                        portage.locks.unlockdir(vdb_lock)
-       all_selected = set()
-       for x in pkgmap:
-               all_selected.update(x["selected"])
+       
+       from portage.sets.base import EditablePackageSet
+       
+       # generate a list of package sets that are directly or indirectly listed in "world",
+       # as there is no persistent list of "installed" sets
+       installed_sets = ["world"]
+       stop = False
+       pos = 0
+       while not stop:
+               stop = True
+               pos = len(installed_sets)
+               for s in installed_sets[pos - 1:]:
+                       candidates = [x[len(SETPREFIX):] for x in sets[s].getNonAtoms() if x.startswith(SETPREFIX)]
+                       if candidates:
+                               stop = False
+                               installed_sets += candidates
+       del stop, pos
+
+       # we don't want to unmerge packages that are still listed in user-editable package sets
+       # listed in "world" as they would be remerged on the next update of "world" or the 
+       # relevant package sets.
+       for cp in xrange(len(pkgmap)):
+               for cpv in pkgmap[cp]["selected"].copy():
+                       parents = []
+                       for s in installed_sets:
+                               # skip sets that the user requested to unmerge, and skip world 
+                               # unless we're unmerging a package set (as the package would be 
+                               # removed from "world" later on)
+                               if s in root_config.setconfig.active or (s == "world" and not root_config.setconfig.active):
+                                       continue
+                               # only check instances of EditablePackageSet as other classes are generally used for
+                               # special purposes and can be ignored here (and are usually generated dynamically, so the
+                               # user can't do much about them anyway)
+                               elif sets[s].containsCPV(cpv) \
+                                       and isinstance(sets[s], EditablePackageSet):
+                                       parents.append(s)
+                       if parents:
+                               #print colorize("WARN", "Package %s is going to be unmerged," % cpv)
+                               #print colorize("WARN", "but still listed in the following package sets:")
+                               #print "    %s\n" % ", ".join(parents)
+                               print colorize("WARN", "Not unmerging package %s as it is" % cpv)
+                               print colorize("WARN", "still referenced by the following package sets:")
+                               print "    %s\n" % ", ".join(parents)
+                               # adjust pkgmap so the display output is correct
+                               pkgmap[cp]["selected"].remove(cpv)
+                               pkgmap[cp]["protected"].add(cpv)
+       
+       del installed_sets
+       
        for x in xrange(len(pkgmap)):
                selected = pkgmap[x]["selected"]
                if not selected:
@@ -5132,15 +5196,14 @@ def unmerge(root_config, myopts, unmerge_action,
                for mytype, mylist in pkgmap[x].iteritems():
                        if mytype == "selected":
                                continue
-                       pkgmap[x][mytype] = \
-                               [cpv for cpv in mylist if cpv not in all_selected]
-               cp = portage.cpv_getkey(selected[0])
+                       mylist.difference_update(all_selected)
+               cp = portage.cpv_getkey(iter(selected).next())
                for y in localtree.dep_match(cp):
                        if y not in pkgmap[x]["omitted"] and \
-                          y not in pkgmap[x]["selected"] and \
-                          y not in pkgmap[x]["protected"] and \
-                          y not in all_selected:
-                               pkgmap[x]["omitted"].append(y)
+                               y not in pkgmap[x]["selected"] and \
+                               y not in pkgmap[x]["protected"] and \
+                               y not in all_selected:
+                               pkgmap[x]["omitted"].add(y)
                if global_unmerge and not pkgmap[x]["selected"]:
                        #avoid cluttering the preview printout with stuff that isn't getting unmerged
                        continue
@@ -5158,15 +5221,14 @@ def unmerge(root_config, myopts, unmerge_action,
                        if "--quiet" not in myopts:
                                portage.writemsg_stdout((mytype + ": ").rjust(14), noiselevel=-1)
                        if pkgmap[x][mytype]:
-                               sorted_pkgs = [portage.catpkgsplit(mypkg)[1:] \
-                                       for mypkg in pkgmap[x][mytype]]
+                               sorted_pkgs = [portage.catpkgsplit(mypkg)[1:] for mypkg in pkgmap[x][mytype]]
                                sorted_pkgs.sort(portage.pkgcmp)
                                for pn, ver, rev in sorted_pkgs:
                                        if rev == "r0":
                                                myversion = ver
                                        else:
                                                myversion = ver + "-" + rev
-                                       if mytype=="selected":
+                                       if mytype == "selected":
                                                portage.writemsg_stdout(
                                                        colorize("UNMERGE_WARN", myversion + " "), noiselevel=-1)
                                        else:
@@ -5204,7 +5266,7 @@ def unmerge(root_config, myopts, unmerge_action,
                for y in pkgmap[x]["selected"]:
                        print ">>> Unmerging "+y+"..."
                        emergelog(xterm_titles, "=== Unmerging... ("+y+")")
-                       mysplit=y.split("/")
+                       mysplit = y.split("/")
                        #unmerge...
                        retval = portage.unmerge(mysplit[0], mysplit[1], settings["ROOT"],
                                mysettings, unmerge_action not in ["clean","prune"],
@@ -6825,6 +6887,18 @@ def action_build(settings, trees, mtimedb,
        if pretend or fetchonly:
                # make the mtimedb readonly
                mtimedb.filename = None
+       if "--digest" in myopts:
+               msg = "The --digest option can prevent corruption from being" + \
+                       " noticed. The `repoman manifest` command is the preferred" + \
+                       " way to generate manifests and it is capable of doing an" + \
+                       " entire repository or category at once."
+               prefix = bad(" * ")
+               writemsg(prefix + "\n")
+               from textwrap import wrap
+               for line in wrap(msg, 72):
+                       writemsg("%s%s\n" % (prefix, line))
+               writemsg(prefix + "\n")
+
        if "--quiet" not in myopts and \
                ("--pretend" in myopts or "--ask" in myopts or \
                "--tree" in myopts or "--verbose" in myopts):
@@ -7442,6 +7516,7 @@ def emerge_main():
                                        sys.stderr.write(line + "\n")
                                return 1
                unmerge_actions = ("unmerge", "prune", "clean", "depclean")
+               
                # In order to know exactly which atoms/sets should be added to the
                # world file, the depgraph performs set expansion later. It will get
                # confused about where the atoms came from if it's not allowed to
@@ -7463,6 +7538,7 @@ def emerge_main():
                                        print "emerge: there are no sets to satisfy %s." % \
                                                colorize("INFORM", s)
                                        return 1
+                               setconfig.active.append(s)
                                if myaction in unmerge_actions and \
                                                not sets[s].supportsOperation("unmerge"):
                                        sys.stderr.write("emerge: the given set %s does " + \
index 2a1a3cfa59a8e090d4ada8687cf9ffc4081ba780..b12411d63c883baa7b5533ee63ed14744fa94735 100644 (file)
@@ -65,6 +65,8 @@ def strip_empty(myarr):
        """
        return [x for x in myarr if x]
 
+_paren_whitespace_re = re.compile(r'\S(\(|\))|(\(|\))\S')
+
 def paren_reduce(mystr,tokenize=1):
        """
        Take a string and convert all paren enclosed entities into sublists, optionally
@@ -83,6 +85,12 @@ def paren_reduce(mystr,tokenize=1):
        @rtype: Array
        @return: The reduced string in an array
        """
+       global _dep_check_strict, _paren_whitespace_re
+       if _dep_check_strict:
+               m = _paren_whitespace_re.search(mystr)
+               if m is not None:
+                       raise portage.exception.InvalidDependString(
+                               "missing space by parenthesis: '%s'" % m.group(0))
        mylist = []
        while mystr:
                left_paren = mystr.find("(")
@@ -446,7 +454,7 @@ def dep_getusedeps( depend ):
                open_bracket = depend.find( '[', open_bracket+1 )
        return use_list
 
-_invalid_atom_chars_regexp = re.compile("[()|?]")
+_invalid_atom_chars_regexp = re.compile("[()|?@]")
 
 def isvalidatom(atom, allow_blockers=False):
        """
index 49b1166a5a421964b91902f458521121fc89562b..02114123498b710d879688106fd12dd2a9177c9a 100644 (file)
@@ -108,7 +108,7 @@ def lockfile(mypath, wantnewlockfile=0, unlinkfile=0, waiting_msg=None):
 
                
        if type(lockfilename) == types.StringType and \
-               myfd != HARDLINK_FD and os.fstat(myfd).st_nlink == 0:
+               myfd != HARDLINK_FD and _fstat_nlink(myfd) == 0:
                # The file was deleted on us... Keep trying to make one...
                os.close(myfd)
                writemsg("lockfile recurse\n",1)
@@ -119,6 +119,22 @@ def lockfile(mypath, wantnewlockfile=0, unlinkfile=0, waiting_msg=None):
        writemsg(str((lockfilename,myfd,unlinkfile))+"\n",1)
        return (lockfilename,myfd,unlinkfile,locking_method)
 
+def _fstat_nlink(fd):
+       """
+       @param fd: an open file descriptor
+       @type fd: Integer
+       @rtype: Integer
+       @return: the current number of hardlinks to the file
+       """
+       try:
+               return os.fstat(fd).st_nlink
+       except EnvironmentError, e:
+               if e.errno == errno.ENOENT:
+                       # Some filesystems such as CIFS return
+                       # ENOENT which means st_nlink == 0.
+                       return 0
+               raise
+
 def unlockfile(mytuple):
        import fcntl
 
@@ -164,7 +180,7 @@ def unlockfile(mytuple):
                        # We won the lock, so there isn't competition for it.
                        # We can safely delete the file.
                        writemsg("Got the lockfile...\n",1)
-                       if os.fstat(myfd).st_nlink == 1:
+                       if _fstat_nlink(myfd) == 1:
                                os.unlink(lockfilename)
                                writemsg("Unlinked lockfile...\n",1)
                                locking_method(myfd,fcntl.LOCK_UN)
index e11a36e4faf16ff1060579d6009984d85e1f5b08..81d204b764937688becb104aebb6bcfd687ae538 100644 (file)
@@ -3,6 +3,7 @@
 # $Id$
 
 import os
+import re
 from itertools import chain
 
 from portage.util import grabfile, write_atomic, ensure_dirs
@@ -19,7 +20,9 @@ __all__ = ["StaticFileSet", "ConfigFileSet", "WorldSet"]
 
 class StaticFileSet(EditablePackageSet):
        _operations = ["merge", "unmerge"]
-       
+       _repopath_match = re.compile(r'.*\$\{repository:(?P<reponame>.+)\}.*')
+       _repopath_sub = re.compile(r'\$\{repository:(?P<reponame>.+)\}')
+               
        def __init__(self, filename, greedy=False, dbapi=None):
                super(StaticFileSet, self).__init__()
                self._filename = filename
@@ -92,17 +95,26 @@ class StaticFileSet(EditablePackageSet):
        def singleBuilder(self, options, settings, trees):
                if not "filename" in options:
                        raise SetConfigError("no filename specified")
-               greedy = get_boolean(options, "greedy", True)
-               return StaticFileSet(options["filename"], greedy=greedy, dbapi=trees["vartree"].dbapi)
+               greedy = get_boolean(options, "greedy", False)
+               filename = options["filename"]
+               # look for repository path variables
+               match = self._repopath_match.match(filename)
+               if match:
+                       filename = self._repopath_sub.sub(trees["porttree"].dbapi.treemap[match.groupdict()["reponame"]], filename)
+               return StaticFileSet(filename, greedy=greedy, dbapi=trees["vartree"].dbapi)
        singleBuilder = classmethod(singleBuilder)
        
        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", "sets/${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", True)
+                       raise SetConfigError("name_pattern doesn't include ${name} placeholder")
+               greedy = get_boolean(options, "greedy", False)
+               # look for repository path variables
+               match = self._repopath_match.match(directory)
+               if match:
+                       directory = self._repopath_sub.sub(trees["porttree"].dbapi.treemap[match.groupdict()["reponame"]], directory)
                if os.path.isdir(directory):
                        for filename in os.listdir(directory):
                                if filename.endswith(".metadata"):