From 2b2527018adce045c71ca0f6d4fc4901ffb68790 Mon Sep 17 00:00:00 2001 From: Fabian Groffen Date: Fri, 8 Jun 2007 07:10:30 +0000 Subject: [PATCH] Merged from trunk 6734:6751 +------------------------------------------------------------------------+ | 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 | 3 +- cnf/make.globals | 4 +- man/emerge.1 | 10 ++- pym/emerge/__init__.py | 19 ++++- pym/emerge/help.py | 13 ++- pym/portage/__init__.py | 91 +++++++++++++++------ pym/portage/dbapi/vartree.py | 152 +++++++++++++---------------------- pym/portage/dep.py | 2 +- pym/portage/output.py | 9 +++ pym/portage/process.py | 23 +++++- pym/portage/versions.py | 7 +- 11 files changed, 195 insertions(+), 138 deletions(-) diff --git a/bin/etc-update b/bin/etc-update index 35720f6a8..9c8188321 100755 --- a/bin/etc-update +++ b/bin/etc-update @@ -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 diff --git a/cnf/make.globals b/cnf/make.globals index a759e3539..effe8f112 100644 --- a/cnf/make.globals +++ b/cnf/make.globals @@ -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" diff --git a/man/emerge.1 b/man/emerge.1 index 70f21459a..39e5669e8 100644 --- a/man/emerge.1 +++ b/man/emerge.1 @@ -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. diff --git a/pym/emerge/__init__.py b/pym/emerge/__init__.py index b61eda860..872758351 100644 --- a/pym/emerge/__init__.py +++ b/pym/emerge/__init__.py @@ -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: diff --git a/pym/emerge/help.py b/pym/emerge/help.py index 7df9ee760..44dea9709 100644 --- a/pym/emerge/help.py +++ b/pym/emerge/help.py @@ -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" diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py index c6173f057..48ef66eba 100644 --- a/pym/portage/__init__.py +++ b/pym/portage/__init__.py @@ -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 diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py index 0eea439c4..445d0d229 100644 --- a/pym/portage/dbapi/vartree.py +++ b/pym/portage/dbapi/vartree.py @@ -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 != "!\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 = ">>>" diff --git a/pym/portage/dep.py b/pym/portage/dep.py index 866e575a0..34291c006 100644 --- a/pym/portage/dep.py +++ b/pym/portage/dep.py @@ -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]])+"\"") diff --git a/pym/portage/output.py b/pym/portage/output.py index f412dd712..46fc77381 100644 --- a/pym/portage/output.py +++ b/pym/portage/output.py @@ -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. diff --git a/pym/portage/process.py b/pym/portage/process.py index dfc106e6b..c3f5d4df4 100644 --- a/pym/portage/process.py +++ b/pym/portage/process.py @@ -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) diff --git a/pym/portage/versions.py b/pym/portage/versions.py index 77273ae48..9b0608430 100644 --- a/pym/portage/versions.py +++ b/pym/portage/versions.py @@ -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 -- 2.26.2