Merged from trunk 6734:6751
authorFabian Groffen <grobian@gentoo.org>
Fri, 8 Jun 2007 07:10:30 +0000 (07:10 -0000)
committerFabian Groffen <grobian@gentoo.org>
Fri, 8 Jun 2007 07:10:30 +0000 (07:10 -0000)
   +------------------------------------------------------------------------+
   | 6735    | Use user-provided $mv_opts for -5 (bug #47442)               |
   | genone  |                                                              |
   |---------+--------------------------------------------------------------|
   | 6736    | Remove duplicate entries from special variables processed by |
   | genone  | env_update() (bug #172194)                                   |
   |---------+--------------------------------------------------------------|
   | 6737    | When dblink.treewalk() looks for an installed instance in    |
   | zmedico | the same slot, make sure that it selects the one with the    |
   |         | highest COUNTER in case there are multiple matches.          |
   |---------+--------------------------------------------------------------|
   | 6738    | Fix typo, split SPACE_SEPARATED variables on whitespace.     |
   | zmedico |                                                              |
   |---------+--------------------------------------------------------------|
   | 6739    | For bug #181021, preserve timestamps from ${D} during merge. |
   | zmedico | For replacement operations, the unmerge phase checks the     |
   |         | contents of the newer instance in order to prevent identical |
   |         | files in the newer instance from being unmerged.             |
   |---------+--------------------------------------------------------------|
   | 6740    | Comment in portage.movefile() that it's impossible to        |
   | zmedico | preserve timestamps of a symlink since utime() only works on |
   |         | the target.                                                  |
   |---------+--------------------------------------------------------------|
   | 6741    | For bug #153967, use -O ${DISTDIR}/${FILE} in the default    |
   | zmedico | FETCHCOMMAND.                                                |
   |---------+--------------------------------------------------------------|
   | 6742    | For bug #156536, update --resume docs to show that the       |
   | zmedico | resume history is capable of storing two merge lists.        |
   |---------+--------------------------------------------------------------|
   | 6743    | For bug #82378, only set permissions on PORT_LOGDIR if it is |
   | zmedico | created automatically.                                       |
   |---------+--------------------------------------------------------------|
   | 6745    | For bug #181021, don't compare attributes of contents        |
   | zmedico | entries when deciding which files not to unmerge. Simply     |
   |         | keep any file that's listed in the new contents in order to  |
   |         | avoid problematic scenarios such as those described in       |
   |         | comments #32 and #40 of bug #8423.                           |
   |---------+--------------------------------------------------------------|
   | 6746    | Return copies of the cached values from pkgsplit and         |
   | zmedico | catpkgsplit.                                                 |
   |---------+--------------------------------------------------------------|
   | 6747    | When using a pty for logging, use setsid() to create a new   |
   | zmedico | session and make the pty into the controlling terminal of    |
   |         | the new session. This makes interactive ebuild behave        |
   |         | properly in interactive cases like check_license() where     |
   |         | ${PAGER:-less} is invoked.                                   |
   |---------+--------------------------------------------------------------|
   | 6748    | Fix strange _DevNull instance has no attribute 'isatty'      |
   | zmedico | warnings that occur when epydoc generates docs.              |
   |---------+--------------------------------------------------------------|
   | 6749    | Make pkgsplit and catpkgsplit return imutable tuples instead |
   | zmedico | of mutable lists so that cached values can be safely         |
   |         | returned without cloning them first. Thanks to Brian Harring |
   |         | for the suggestion.                                          |
   |---------+--------------------------------------------------------------|
   | 6750    | Make all the python code respect USE conditionals in         |
   | zmedico | RESTRICT. It's already handled on the bash side since        |
   |         | RESTRICT is assigned the value of PORTAGE_RESTRICT.          |
   |---------+--------------------------------------------------------------|
   | 6751    | Ignore InvalidDependString exceptions from the RESTRICT of   |
   | zmedico | "nomerge" nodes.                                             |
   +------------------------------------------------------------------------+

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

bin/etc-update
cnf/make.globals
man/emerge.1
pym/emerge/__init__.py
pym/emerge/help.py
pym/portage/__init__.py
pym/portage/dbapi/vartree.py
pym/portage/dep.py
pym/portage/output.py
pym/portage/process.py
pym/portage/versions.py

index 35720f6a83af3b46722ce8ca01a064173f4cd2ae..9c8188321010318db2fcb381f5ef32d1e958b48f 100755 (executable)
@@ -176,7 +176,8 @@ configuration file is followed by a list of possible replacement files."
                        fi
                        if [[ ${input} == -5 ]] ; then
                                input=-3
-                               export mv_opts=""
+                               export mv_opts=" ${mv_opts} "
+                               mv_opts="${mv_opts// -i / }"
                        fi
                        if [[ ${input} == -3 ]] ; then
                                input=0
index a759e35396743e557a38cd2088205aa7a9bcb004..effe8f112b267abcfdd2af6eba69d570c24aa1ec 100644 (file)
@@ -29,8 +29,8 @@ RPMDIR=@DOMAIN_PREFIX@/usr/portage/rpm
 PORTAGE_TMPDIR=@DOMAIN_PREFIX@/var/tmp
 
 # Fetching command (5 tries, passive ftp for firewall compatibility)
-FETCHCOMMAND="wget -t 5 --passive-ftp -P \${DISTDIR} \${URI}"
-RESUMECOMMAND="wget -c -t 5 --passive-ftp -P \${DISTDIR} \${URI}"
+FETCHCOMMAND="wget -t 5 -T 60 --passive-ftp -O \${DISTDIR}/\${FILE} \${URI}"
+RESUMECOMMAND="wget -c -t 5 -T 60 --passive-ftp -O \${DISTDIR}/\${FILE} \${URI}"
 
 # Default user options
 FEATURES="sandbox distlocks metadata-transfer"
index 70f21459ad3ed55477d3be64f7eec87aecf8b684..39e5669e892f8146e38798dd3e28ffdb8285e19c 100644 (file)
@@ -144,9 +144,13 @@ the cache.  After a portage update, rsync users may find it convenient to run
 a sync operation.
 .TP
 .BR "\-\-resume"
-Resumes the last merge operation.  Please note that this operation
-will only return an error on failure.  If there is nothing for portage
-to do, then portage will exit with a message and a success condition.
+Resumes the most recent merge list that has been aborted due to an error.
+Please note that this operation will only return an error on failure.  If there
+is nothing for portage to do, then portage will exit with a message and a
+success condition. A resume list will persist until it has been completed in
+entirety or until another aborted merge list replaces it.  The resume history
+is capable of storing two merge lists.  After one resume list completes, it is
+possible to invoke \-\-resume once again in order to resume an older list.
 .TP
 .BR "\-\-search " (\fB\-s\fR)
 Searches for matches of the supplied string in the portage tree.
index b61eda86016659a39d1fdb9754dafe511bade7f4..8727583518d363d022ab83cb91de94794e489911 100644 (file)
@@ -2451,6 +2451,8 @@ class depgraph:
                                depth >= mylist[i+1][1]:
                                        del mylist[i]
 
+               from portage import flatten
+               from portage.dep import use_reduce, paren_reduce
                display_overlays=False
                # files to fetch list - avoids counting a same file twice
                # in size display (verbose mode)
@@ -2489,6 +2491,7 @@ class depgraph:
                                blockers.append(addl)
                        else:
                                mydbapi = self.trees[myroot][self.pkg_tree_map[pkg_type]].dbapi
+                               pkg_status = x[3]
                                binary_package = True
                                if "ebuild" == pkg_type:
                                        if "merge" == x[3] or \
@@ -2509,9 +2512,19 @@ class depgraph:
                                        self.useFlags[myroot][pkg_key] = mydbapi.aux_get(
                                                pkg_key, ["USE"])[0].split()
 
+                               try:
+                                       restrict = flatten(use_reduce(paren_reduce(
+                                               mydbapi.aux_get(pkg_key, ["RESTRICT"])[0]),
+                                               uselist=self.useFlags[myroot][pkg_key]))
+                               except portage.exception.InvalidDependString, e:
+                                       if pkg_status != "nomerge":
+                                               restrict = mydbapi.aux_get(pkg_key, ["RESTRICT"])[0]
+                                               show_invalid_depstring_notice(x, restrict, str(e))
+                                               del e
+                                               sys.exit(1)
+                                       restrict = []
                                if "ebuild" == pkg_type and x[3] != "nomerge" and \
-                                       "fetch" in portdb.aux_get(
-                                       x[2], ["RESTRICT"])[0].split():
+                                       "fetch" in restrict:
                                        fetch = red("F")
                                        if ordered:
                                                counters.restrict_fetch += 1
@@ -2685,7 +2698,7 @@ class depgraph:
                                        else:
                                                verboseadd += "[No ebuild?]"
 
-                               xs=portage.pkgsplit(x[2])
+                               xs = list(portage.pkgsplit(x[2]))
                                if xs[2]=="r0":
                                        xs[2]=""
                                else:
index 7df9ee76037af7f71236161b6043b7ff3b805d89..44dea9709dd7373226fa18fb4207b5a796128395 100644 (file)
@@ -100,10 +100,15 @@ def help(myaction,myopts,havecolor=1):
                print "              Rsync users should simply 'emerge --sync' to regenerate."
                print
                print "       "+green("--resume")
-               print "              Resumes the last merge operation. It can be treated just like a"
-               print "              regular emerge: --pretend and other options work alongside it."
-               print "              'emerge --resume' only returns an error on failure. When there is"
-               print "              nothing to do, it exits with a message and a success condition."
+               print "              Resumes the most recent merge list that has been aborted due to an"
+               print "              error. Please note that this operation will only return an error"
+               print "              on failure. If there is nothing for portage to do, then portage"
+               print "              will exit with a message and a success condition. A resume list"
+               print "              will persist until it has been completed in entirety or until"
+               print "              another aborted merge list replaces it. The resume history is"
+               print "              capable of storing two merge lists. After one resume list"
+               print "              completes, it is possible to invoke --resume once again in order"
+               print "              to resume an older list."
                print
                print "       "+green("--search")+" ("+green("-s")+" short option)"
                print "              Searches for matches of the supplied string in the current local"
index c6173f057fc23c385d658a4d3f9975d80a78b92e..48ef66eba357e1c76ae69ea264b4aff2261060cd 100644 (file)
@@ -518,7 +518,9 @@ def env_update(makelinks=1, target_root=None, prev_mtimes=None, contents=None):
                mylist = []
                for myconfig in config_list:
                        if var in myconfig:
-                               mylist.extend(filter(None, myconfig[var].split()))
+                               for item in myconfig[var].split():
+                                       if item and not item in mylist:
+                                               mylist.append(item)
                                del myconfig[var] # prepare for env.update(myconfig)
                if mylist:
                        env[var] = " ".join(mylist)
@@ -528,7 +530,9 @@ def env_update(makelinks=1, target_root=None, prev_mtimes=None, contents=None):
                mylist = []
                for myconfig in config_list:
                        if var in myconfig:
-                               mylist.extend(filter(None, myconfig[var].split(":")))
+                               for item in myconfig[var].split(":"):
+                                       if item and not item in mylist:
+                                               mylist.append(item)
                                del myconfig[var] # prepare for env.update(myconfig)
                if mylist:
                        env[var] = ":".join(mylist)
@@ -2295,6 +2299,8 @@ def spawn(mystring, mysettings, debug=0, free=0, droppriv=0, sesandbox=0, **keyw
        slave_fd = None
        output_pid = None
        input_pid = None
+       stdin_termios = None
+       stdin_fd = None
        if logfile:
                del keywords["logfile"]
                fd_pipes = keywords.get("fd_pipes")
@@ -2304,12 +2310,41 @@ def spawn(mystring, mysettings, debug=0, free=0, droppriv=0, sesandbox=0, **keyw
                        raise ValueError(fd_pipes)
                from pty import openpty
                master_fd, slave_fd = openpty()
-               # Disable the ECHO attribute so the terminal behaves properly
-               # if the subprocess needs to read input from stdin.
-               import termios
-               term_attr = termios.tcgetattr(slave_fd)
-               term_attr[3] &= ~termios.ECHO
-               termios.tcsetattr(slave_fd, termios.TCSAFLUSH, term_attr)
+               fd_pipes.setdefault(0, sys.stdin.fileno())
+               stdin_fd = fd_pipes[0]
+               if os.isatty(stdin_fd):
+                       # Copy the termios attributes from stdin_fd to the slave_fd and put
+                       # the stdin_fd into raw mode with ECHO disabled.  The stdin
+                       # termios attributes are reverted before returning, or via the
+                       # atexit hook in portage.process when killed by a signal.
+                       import termios, tty
+                       stdin_termios = termios.tcgetattr(stdin_fd)
+                       tty.setraw(stdin_fd)
+                       term_attr = termios.tcgetattr(stdin_fd)
+                       term_attr[3] &= ~termios.ECHO
+                       termios.tcsetattr(stdin_fd, termios.TCSAFLUSH, term_attr)
+                       termios.tcsetattr(slave_fd, termios.TCSAFLUSH, stdin_termios)
+                       from output import get_term_size, set_term_size
+                       rows, columns = get_term_size()
+                       set_term_size(rows, columns, slave_fd)
+                       pre_exec = keywords.get("pre_exec")
+                       def setup_ctty():
+                               os.setsid()
+                               # Make it into the "controlling terminal".
+                               import termios
+                               if hasattr(termios, "TIOCSCTTY"):
+                                       # BSD 4.3 approach
+                                       import fcntl
+                                       fcntl.ioctl(0, termios.TIOCSCTTY)
+                               else:
+                                       # SVR4 approach
+                                       fd = os.open(os.ttyname(0), os.O_RDWR)
+                                       for x in 0, 1, 2:
+                                               os.dup2(fd, x)
+                                       os.close(fd)
+                               if pre_exec:
+                                       pre_exec()
+                       keywords["pre_exec"] = setup_ctty
                # tee will always exit with an IO error, so ignore it's stderr.
                null_file = open('/dev/null', 'w')
                mypids.extend(portage.process.spawn(['tee', '-i', '-a', logfile],
@@ -2328,7 +2363,7 @@ def spawn(mystring, mysettings, debug=0, free=0, droppriv=0, sesandbox=0, **keyw
                keywords["fd_pipes"] = fd_pipes
 
        features = mysettings.features
-       restrict = mysettings.get("RESTRICT", "").split()
+       restrict = mysettings.get("PORTAGE_RESTRICT","").split()
        droppriv=(droppriv and "userpriv" in features and not \
                ("nouserpriv" in restrict or "userpriv" in restrict))
        if droppriv and not uid and portage_gid and portage_uid:
@@ -2379,8 +2414,12 @@ def spawn(mystring, mysettings, debug=0, free=0, droppriv=0, sesandbox=0, **keyw
                os.waitpid(input_pid, 0)
                portage.process.spawned_pids.remove(input_pid)
        pid = mypids[-1]
-       retval = os.waitpid(pid, 0)[1]
-       portage.process.spawned_pids.remove(pid)
+       try:
+               retval = os.waitpid(pid, 0)[1]
+               portage.process.spawned_pids.remove(pid)
+       finally:
+               if stdin_termios:
+                       termios.tcsetattr(stdin_fd, termios.TCSAFLUSH, stdin_termios)
        if retval != os.EX_OK:
                if retval & 0xff:
                        return (retval & 0xff) << 8
@@ -2391,9 +2430,10 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0, locks_in_subdir=".locks",
        "fetch files.  Will use digest file if available."
 
        features = mysettings.features
+       restrict = mysettings.get("PORTAGE_RESTRICT","").split()
        # 'nomirror' is bad/negative logic. You Restrict mirroring, not no-mirroring.
-       if ("mirror" in mysettings["RESTRICT"].split()) or \
-          ("nomirror" in mysettings["RESTRICT"].split()):
+       if "mirror" in restrict or \
+          "nomirror" in restrict:
                if ("mirror" in features) and ("lmirror" not in features):
                        # lmirror should allow you to bypass mirror restrictions.
                        # XXX: This is not a good thing, and is temporary at best.
@@ -2428,8 +2468,8 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0, locks_in_subdir=".locks",
        if custommirrors.has_key("local"):
                mymirrors += custommirrors["local"]
 
-       if ("nomirror" in mysettings["RESTRICT"].split()) or \
-          ("mirror"   in mysettings["RESTRICT"].split()):
+       if "nomirror" in restrict or \
+          "mirror" in restrict:
                # We don't add any mirrors.
                pass
        else:
@@ -2450,7 +2490,7 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0, locks_in_subdir=".locks",
                        fsmirrors += [mymirrors[x]]
                        del mymirrors[x]
 
-       restrict_fetch = "fetch" in mysettings["RESTRICT"].split()
+       restrict_fetch = "fetch" in restrict
        custom_local_mirrors = custommirrors.get("local", [])
        if restrict_fetch:
                # With fetch restriction, a normal uri may only be fetched from
@@ -2497,7 +2537,7 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0, locks_in_subdir=".locks",
                        if restrict_fetch:
                                # Only fetch from specific mirrors is allowed.
                                continue
-                       if "primaryuri" in mysettings["RESTRICT"].split():
+                       if "primaryuri" in restrict:
                                # Use the source site first.
                                if primaryuri_indexes.has_key(myfile):
                                        primaryuri_indexes[myfile] += 1
@@ -3373,8 +3413,10 @@ def prepare_build_dirs(myroot, mysettings, cleanup):
                        del mysettings["PORT_LOGDIR"]
        if "PORT_LOGDIR" in mysettings:
                try:
-                       portage.util.ensure_dirs(mysettings["PORT_LOGDIR"],
-                               uid=portage_uid, gid=portage_gid, mode=02770)
+                       modified = portage.util.ensure_dirs(mysettings["PORT_LOGDIR"])
+                       if modified:
+                               apply_secpass_permissions(mysettings["PORT_LOGDIR"],
+                                       uid=portage_uid, gid=portage_gid, mode=02770)
                except portage.exception.PortageException, e:
                        writemsg("!!! %s\n" % str(e), noiselevel=-1)
                        writemsg("!!! Permission issues with PORT_LOGDIR='%s'\n" % \
@@ -3816,13 +3858,14 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0,
 
                #initial dep checks complete; time to process main commands
 
+               restrict = mysettings["PORTAGE_RESTRICT"].split()
                nosandbox = (("userpriv" in features) and \
                        ("usersandbox" not in features) and \
-                       ("userpriv" not in mysettings["RESTRICT"]) and \
-                       ("nouserpriv" not in mysettings["RESTRICT"]))
+                       "userpriv" not in restrict and \
+                       "nouserpriv" not in restrict)
                if nosandbox and ("userpriv" not in features or \
-                       "userpriv" in mysettings["RESTRICT"] or \
-                       "nouserpriv" in mysettings["RESTRICT"]):
+                       "userpriv" in restrict or \
+                       "nouserpriv" in restrict):
                        nosandbox = ("sandbox" not in features and \
                                "usersandbox" not in features)
 
@@ -3979,6 +4022,8 @@ def movefile(src,dest,newmtime=None,sstat=None,mysettings=None):
                        else:
                                os.symlink(target,dest)
                        lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
+                       # utime() only works on the target of a symlink, so it's not
+                       # possible to perserve mtime on symlinks.
                        return os.lstat(dest)[stat.ST_MTIME]
                except SystemExit, e:
                        raise
index 0eea439c40327e934c27e9d8fa787ceeba27a15d..445d0d229103d5eaa8c6fa541c2637dbdcb65863 100644 (file)
@@ -852,9 +852,9 @@ class dblink(object):
                        mysettings.get("CONFIG_PROTECT","").split(),
                        mysettings.get("CONFIG_PROTECT_MASK","").split())
                self.updateprotect = protect_obj.updateprotect
-               self._config_protect = protect_obj
+               self.isprotected = protect_obj.isprotected
                self._installed_instance = None
-               self.contentscache=[]
+               self.contentscache = None
                self._contents_inodes = None
 
        def lockdb(self):
@@ -910,7 +910,7 @@ class dblink(object):
                contents_file = os.path.join(self.dbdir, "CONTENTS")
                if not os.path.exists(contents_file):
                        return None
-               if self.contentscache != []:
+               if self.contentscache is not None:
                        return self.contentscache
                pkgfiles = {}
                myc = open(contents_file,"r")
@@ -968,7 +968,7 @@ class dblink(object):
                return pkgfiles
 
        def unmerge(self, pkgfiles=None, trimworld=1, cleanup=1,
-               ldpath_mtimes=None):
+               ldpath_mtimes=None, new_contents=None):
                """
                Calls prerm
                Unmerges a given package (CPV)
@@ -984,6 +984,8 @@ class dblink(object):
                @type cleanup: Boolean
                @param ldpath_mtimes: mtimes to pass to env_update (see env_update)
                @type ldpath_mtimes: Dictionary
+               @param new_contents: contents from a new instance that will replace this one
+               @type new_contents: Dictionary
                @rtype: Integer
                @returns:
                1. os.EX_OK if everything went well.
@@ -1047,7 +1049,7 @@ class dblink(object):
                                        writemsg("!!! FAILED prerm: %s\n" % retval, noiselevel=-1)
                                        return retval
 
-                       self._unmerge_pkgfiles(pkgfiles)
+                       self._unmerge_pkgfiles(pkgfiles, new_contents=new_contents)
                        
                        # Remove the registration of preserved libs for this pkg instance
                        self.vartree.dbapi.plib_registry.unregister(self.mycpv, self.settings["SLOT"], self.settings["COUNTER"])
@@ -1092,7 +1094,7 @@ class dblink(object):
                        contents=contents)
                return os.EX_OK
 
-       def _unmerge_pkgfiles(self, pkgfiles):
+       def _unmerge_pkgfiles(self, pkgfiles, new_contents=None):
                """
                
                Unmerges the contents of a package from the liveFS
@@ -1100,6 +1102,8 @@ class dblink(object):
                
                @param pkgfiles: typically self.getcontents()
                @type pkgfiles: Dictionary { filename: [ 'type', '?', 'md5sum' ] }
+               @param new_contents: contents from a new instance that will replace this one
+               @type new_contents: Dictionary
                @rtype: None
                """
                global dircache
@@ -1109,6 +1113,28 @@ class dblink(object):
                        writemsg_stdout("No package files given... Grabbing a set.\n")
                        pkgfiles = self.getcontents()
 
+               if not new_contents:
+                       counter = self.vartree.dbapi.cpv_counter(self.mycpv)
+                       slot = self.vartree.dbapi.aux_get(self.mycpv, ["SLOT"])[0]
+                       slot_matches = self.vartree.dbapi.match(
+                               "%s:%s" % (dep_getkey(self.mycpv), slot))
+                       new_cpv = None
+                       if slot_matches:
+                               max_counter = -1
+                               for cur_cpv in slot_matches:
+                                       cur_counter = self.vartree.dbapi.cpv_counter(cur_cpv)
+                                       if cur_counter == counter and \
+                                               cur_cpv == self.mycpv:
+                                               continue
+                                       if cur_counter > max_counter:
+                                               max_counter = cur_counter
+                                               new_cpv = cur_cpv
+                       if new_cpv:
+                               # The current instance has been replaced by a newer instance.
+                               new_cat, new_pkg = catsplit(new_cpv)
+                               new_contents = dblink(new_cat, new_pkg, self.vartree.root,
+                                       self.settings, vartree=self.vartree).getcontents()
+
                if pkgfiles:
                        mykeys = pkgfiles.keys()
                        mykeys.sort()
@@ -1119,8 +1145,12 @@ class dblink(object):
                        modprotect = "/lib/modules/"
                        for objkey in mykeys:
                                obj = normalize_path(objkey)
-                               if obj[:2] == "//":
-                                       obj = obj[1:]
+                               if new_contents and obj in new_contents:
+                                       # A new instance of this package claims the file, so don't
+                                       # unmerge it.
+                                       writemsg_stdout("--- !owned %s %s\n" % \
+                                               (pkgfiles[objkey][0], obj))
+                                       continue
                                statobj = None
                                try:
                                        statobj = os.stat(obj)
@@ -1254,32 +1284,6 @@ class dblink(object):
 
                return False
 
-       def isprotected(self, filename):
-               """In cases where an installed package in the same slot owns a
-               protected file that will be merged, bump the mtime on the installed
-               file in order to ensure that it isn't unmerged."""
-               if not self._config_protect.isprotected(filename):
-                       return False
-               if self._installed_instance is None:
-                       return True
-               mydata = self._installed_instance.getcontents().get(filename, None)
-               if mydata is None:
-                       return True
-
-               # Bump the mtime in order to ensure that the old config file doesn't
-               # get unmerged.  The user will have an opportunity to merge the new
-               # config with the old one.
-               try:
-                       os.utime(filename, None)
-               except OSError, e:
-                       if e.errno != errno.ENOENT:
-                               raise
-                       del e
-                       # The file has disappeared, so it's not protected.
-                       return False
-               return True
-
-
        def _preserve_libs(self, srcroot, destroot, mycontents, counter):
                # read global reverse NEEDED map
                libmap = self.vartree.dbapi.libmap.get()
@@ -1505,6 +1509,14 @@ class dblink(object):
                        "%s:%s" % (self.mysplit[0], self.settings["SLOT"]))
                if slot_matches:
                        # Used by self.isprotected().
+                       max_cpv = None
+                       max_counter = -1
+                       for cur_cpv in slot_matches:
+                               cur_counter = self.vartree.dbapi.cpv_counter(cur_cpv)
+                               if cur_counter > max_counter:
+                                       max_counter = cur_counter
+                                       max_cpv = cur_cpv
+                       slot_matches = [max_cpv]
                        self._installed_instance = dblink(self.cat,
                                catsplit(slot_matches[0])[1], destroot, self.settings,
                                vartree=self.vartree)
@@ -1547,9 +1559,6 @@ class dblink(object):
                                        raise
                                del e
 
-               # get old contents info for later unmerging
-               oldcontents = self.getcontents()
-
                self.dbdir = self.dbtmpdir
                self.delete()
                if not os.path.exists(self.dbtmpdir):
@@ -1591,12 +1600,11 @@ class dblink(object):
                else:
                        cfgfiledict["IGNORE"]=0
 
-               # Timestamp for files being merged.  Use time() - 1 in order to prevent
-               # a collision with timestamps that are bumped by the utime() call
-               # inside isprotected().  This ensures that the new and old config have
-               # different timestamps (for the benefit of programs like rsync that
-               # that need distiguishable timestamps to detect file changes).
-               mymtime = long(time.time() - 1)
+               # Don't bump mtimes on merge since some application require
+               # preservation of timestamps.  This means that the unmerge phase must
+               # check to see if file belongs to an installed instance in the same
+               # slot.
+               mymtime = None
 
                # set umask to 0 for merging; back up umask, save old one in prevmask (since this is a global change)
                prevmask = os.umask(0)
@@ -1635,12 +1643,14 @@ class dblink(object):
                #if we opened it, close it
                outfile.flush()
                outfile.close()
+               self.contentscache = None
+               new_contents = self.getcontents()
 
                if os.path.exists(self.dbpkgdir):
                        writemsg_stdout(">>> Safely unmerging already-installed instance...\n")
-                       self.dbdir = self.dbpkgdir
-                       self.unmerge(oldcontents, trimworld=0, ldpath_mtimes=prev_mtimes)
-                       self.dbdir = self.dbtmpdir
+                       dblink(self.cat, self.pkg, destroot, self.settings,
+                               vartree=self.vartree).unmerge(trimworld=0,
+                                       ldpath_mtimes=prev_mtimes, new_contents=new_contents)
                        writemsg_stdout(">>> Original instance of package unmerged safely.\n")
 
                # We hold both directory locks.
@@ -1909,9 +1919,7 @@ class dblink(object):
                                                        destmd5 = perform_md5(mydest, calc_prelink=1)
                                                        if mymd5 == destmd5:
                                                                #file already in place; simply update mtimes of destination
-                                                               os.utime(mydest, (thismtime, thismtime))
-                                                               zing = "---"
-                                                               moveme = 0
+                                                               moveme = 1
                                                        else:
                                                                if mymd5 == cfgfiledict.get(myrealdest, [None])[0]:
                                                                        """ An identical update has previously been
@@ -1940,52 +1948,6 @@ class dblink(object):
                                        if mymtime is None:
                                                sys.exit(1)
                                        zing = ">>>"
-                               else:
-                                       mymtime = long(time.time())
-                                       # We need to touch the destination so that on --update the
-                                       # old package won't yank the file with it. (non-cfgprot related)
-                                       os.utime(mydest, (mymtime, mymtime))
-                                       zing = "---"
-                               if self.settings["USERLAND"] == "Darwin" and myrealdest[-2:] == ".a":
-
-                                       # XXX kludge, can be killed when portage stops relying on
-                                       # md5+mtime, and uses refcounts
-                                       # alright, we've fooled w/ mtime on the file; this pisses off static archives
-                                       # basically internal mtime != file's mtime, so the linker (falsely) thinks
-                                       # the archive is stale, and needs to have it's toc rebuilt.
-
-                                       myf = open(mydest, "r+")
-
-                                       # ar mtime field is digits padded with spaces, 12 bytes.
-                                       lms = str(thismtime+5).ljust(12)
-                                       myf.seek(0)
-                                       magic = myf.read(8)
-                                       if magic != "!<arch>\n":
-                                               # not an archive (dolib.a from portage.py makes it here fex)
-                                               myf.close()
-                                       else:
-                                               st = os.stat(mydest)
-                                               while myf.tell() < st.st_size - 12:
-                                                       # skip object name
-                                                       myf.seek(16, 1)
-
-                                                       # update mtime
-                                                       myf.write(lms)
-
-                                                       # skip uid/gid/mperm
-                                                       myf.seek(20, 1)
-
-                                                       # read the archive member's size
-                                                       x = long(myf.read(10))
-
-                                                       # skip the trailing newlines, and add the potential
-                                                       # extra padding byte if it's not an even size
-                                                       myf.seek(x + 2 + (x % 2),1)
-
-                                               # and now we're at the end. yay.
-                                               myf.close()
-                                               mymd5 = perform_md5(mydest, calc_prelink=1)
-                                       os.utime(mydest, (thismtime, thismtime))
 
                                if mymtime != None:
                                        zing = ">>>"
index 866e575a091fe175563f0f95dc1b29a34cf48946..34291c006567cf15b4f8bf5323ad28d8b9ab96b5 100644 (file)
@@ -263,7 +263,7 @@ def use_reduce(deparray, uselist=[], masklist=[], matchall=0, excludeall=[]):
                                        else:
                                                ismatch = False
                                if missing_flag:
-                                       raise portage_exception.InvalidDependString(
+                                       raise portage.exception.InvalidDependString(
                                                "Conditional without flag: \"" + \
                                                paren_enclose([head+"?", newdeparray[-1]])+"\"")
 
index f412dd712bdbca4c89827fa8e1e889e4cb4dfd16..46fc7738129d2a7468966d0a7146c3063923915e 100644 (file)
@@ -260,6 +260,15 @@ def get_term_size():
                                pass
        return -1, -1
 
+def set_term_size(lines, columns, fd):
+       """
+       Set the number of lines and columns for the tty that is connected to fd.
+       For portability, this simply calls `stty rows $lines columns $columns`.
+       """
+       from portage.process import spawn
+       cmd = ["stty", "rows", str(lines), "columns", str(columns)]
+       spawn(cmd, env=os.environ, fd_pipes={0:fd})
+
 class EOutput:
        """
        Performs fancy terminal formatting for status and informational messages.
index dfc106e6bdd4705afdc6afe2689d064458ef1087..c3f5d4df4e959ade9ef06f556a19803f4251fbb1 100644 (file)
@@ -111,9 +111,19 @@ def cleanup():
 
 atexit_register(cleanup)
 
+# Make sure the original terminal attributes are reverted at exit.
+if hasattr(sys.stdin, "isatty") and sys.stdin.isatty():
+       import termios
+       _stdin_termios = termios.tcgetattr(sys.stdin.fileno())
+       def _reset_stdin_termios(stdin_termios):
+               import termios
+               termios.tcsetattr(sys.stdin.fileno(), termios.TCSAFLUSH, stdin_termios)
+       atexit_register(_reset_stdin_termios, _stdin_termios)
+       del termios, _stdin_termios, _reset_stdin_termios
+
 def spawn(mycommand, env={}, opt_name=None, fd_pipes=None, returnpid=False,
           uid=None, gid=None, groups=None, umask=None, logfile=None,
-          path_lookup=True):
+          path_lookup=True, pre_exec=None):
        """
        Spawns a given command.
        
@@ -140,6 +150,8 @@ def spawn(mycommand, env={}, opt_name=None, fd_pipes=None, returnpid=False,
        @type logfile: String
        @param path_lookup: If the binary is not fully specified then look for it in PATH
        @type path_lookup: Boolean
+       @param pre_exec: A function to be called with no arguments just prior to the exec call.
+       @type pre_exec: callable
        
        logfile requires stdout and stderr to be assigned to this process (ie not pointed
           somewhere else.)
@@ -194,7 +206,7 @@ def spawn(mycommand, env={}, opt_name=None, fd_pipes=None, returnpid=False,
        if not pid:
                try:
                        _exec(binary, mycommand, opt_name, fd_pipes,
-                             env, gid, groups, uid, umask)
+                             env, gid, groups, uid, umask, pre_exec)
                except Exception, e:
                        # We need to catch _any_ exception so that it doesn't
                        # propogate out of this function and cause exiting
@@ -251,7 +263,8 @@ def spawn(mycommand, env={}, opt_name=None, fd_pipes=None, returnpid=False,
        # Everything succeeded
        return 0
 
-def _exec(binary, mycommand, opt_name, fd_pipes, env, gid, groups, uid, umask):
+def _exec(binary, mycommand, opt_name, fd_pipes, env, gid, groups, uid, umask,
+       pre_exec):
 
        """
        Execute a given binary with options
@@ -274,6 +287,8 @@ def _exec(binary, mycommand, opt_name, fd_pipes, env, gid, groups, uid, umask):
        @type uid: Integer
        @param umask: an int representing a unix umask (see man chmod for umask details)
        @type umask: Integer
+       @param pre_exec: A function to be called with no arguments just prior to the exec call.
+       @type pre_exec: callable
        @rtype: None
        @returns: Never returns (calls os.execve)
        """
@@ -315,6 +330,8 @@ def _exec(binary, mycommand, opt_name, fd_pipes, env, gid, groups, uid, umask):
                os.setuid(uid)
        if umask:
                os.umask(umask)
+       if pre_exec:
+               pre_exec()
 
        # And switch to the new process.
        os.execve(binary, myargs, env)
index 77273ae48b274c82be5be6b85b4ce9416bbbbd8a..9b0608430aae708e43173333eda9b2f0b0ac42fd 100644 (file)
@@ -221,7 +221,7 @@ def pkgsplit(mypkg,silent=1):
        try:
                if not pkgcache[mypkg]:
                        return None
-               return pkgcache[mypkg][:]
+               return pkgcache[mypkg]
        except KeyError:
                pass
        myparts=mypkg.split("-")
@@ -265,7 +265,7 @@ def pkgsplit(mypkg,silent=1):
                                        pkgcache[mypkg]=None
                                        return None
                                        #names can't have versiony looking parts
-                       myval=["-".join(myparts[:verPos]),myparts[verPos],revision]
+                       myval=("-".join(myparts[:verPos]),myparts[verPos],revision)
                        pkgcache[mypkg]=myval
                        return myval
        else:
@@ -295,7 +295,7 @@ def catpkgsplit(mydata,silent=1):
        try:
                if not catcache[mydata]:
                        return None
-               return catcache[mydata][:]
+               return catcache[mydata]
        except KeyError:
                pass
        mysplit=mydata.split("/")
@@ -310,6 +310,7 @@ def catpkgsplit(mydata,silent=1):
                catcache[mydata]=None
                return None
        retval.extend(p_split)
+       retval = tuple(retval)
        catcache[mydata]=retval
        return retval