Merged from trunk 8729:8754
authorFabian Groffen <grobian@gentoo.org>
Sun, 2 Dec 2007 21:29:30 +0000 (21:29 -0000)
committerFabian Groffen <grobian@gentoo.org>
Sun, 2 Dec 2007 21:29:30 +0000 (21:29 -0000)
   | 8731    | If portage is reinstalling itself, create a temporary copy   |
   | zmedico | of PORTAGE_BIN_PATH in order to avoid relying on on the new  |
   |         | versions which may be incompatible.                          |

   | 8732    | Enable the EBUILD_EXIT_STATUS_FILE sanity check when portage |
   | zmedico | is reinstalling itself since the check is reliable in this   |
   |         | case now that a temporary PORTAGE_BIN_PATH is created.       |

   | 8733    | Fix references to PORTAGE_BIN_PATH in doebuild() to use the  |
   | zmedico | config setting since it may be a temp directory when portage |
   |         | is reinstalling itself.                                      |

   | 8734    | In doebuild, fix paths to ebuild.sh and misc-functions.sh so |
   | zmedico | that they work with variable PORTAGE_BIN_PATH.               |

   | 8735    | Fix more references to MISC_SH_BINARY so that they work      |
   | zmedico | properly with variable PORTAGE_BIN_PATH.                     |

   | 8736    | Fix a typo.                                                  |
   | zmedico |                                                              |

   | 8737    | Make dblink.treewalk() properly delete the installed         |
   | zmedico | instance after it unmerges it.                               |

   | 8738    | Fix references to EBUILD_SH_BINARY so that they work         |
   | zmedico | properly with variable PORTAGE_BIN_PATH.                     |

   | 8739    | Remove stray print statement.                                |
   | zmedico |                                                              |

   | 8741    | In dblink.treewalk(), make portage unmerge multiple          |
   | zmedico | instances os sys-apps/portage in the same slot if necessary  |
   |         | (needed when AUTOCLEAN=no is set).                           |

   | 8743    | When portage reinstalls itself, copy both the bin and pym    |
   | zmedico | directories to a temp dir. Insert the temporary              |
   |         | PORTAGE_PYM_PATH as the first element of sys.path and        |
   |         | register an atexit hook to clean up the temporary            |
   |         | directories.                                                 |

   | 8745    | Adjust PYTHONPATH when calling portageq so that variable     |
   | zmedico | PORTAGE_PYM_PATH works.                                      |

   | 8747    | Don't modify sys.path inside dblink.merge() because it       |
   | zmedico | doesn't seem to help and it triggers import errors for elog  |
   |         | modules when downgrading to versions of portage that use the |
   |         | old namespace.                                               |

   | 8749    | When portage reinstalls itself, pre-load elog modules in     |
   | zmedico | dblink.merge() since we won't be able to later if they get   |
   |         | unmerged (happens when namespace changes).                   |

   | 8751    | Make elog_process() pre-load log modules that it can be      |
   | zmedico | called just for that purpose.                                |

   | 8753    | Move environment.bz2 extraction from ebuild.sh to doebuild() |
   | zmedico | on the python side. The python will be able to use it's      |
   |         | awareness of the ${T}/environment to decide what type of     |
   |         | ebuild environment should be generated. For example, if the  |
   |         | ebuild environment should be able to unset variables that    |
   |         | have been inherited from the calling environment, the        |
   |         | existence of ${T}/environment will indicate that the ebuild  |
   |         | environment should be isolated from the calling environment. |

   | 8754    | Bug #189417 - When ${T}/environment exists, isolate the      |
   | zmedico | ebuild environment from the calling environment. This makes  |
   |         | it possible for the build to unset a variable that was       |
   |         | inherited from the calling environment, and the variable     |
   |         | will remain unset between phases.                            |

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

bin/ebuild.sh
bin/isolated-functions.sh
pym/portage/__init__.py
pym/portage/dbapi/vartree.py
pym/portage/elog/__init__.py

index 317507d3b230e7eb833402097136d8f820713a17..5ae30bda9d9ef9407fa49693de2f74f23947eddd 100755 (executable)
@@ -150,6 +150,7 @@ has_version() {
        fi
        # return shell-true/shell-false if exists.
        # Takes single depend-type atoms.
+       PYTHONPATH="${PORTAGE_PYM_PATH}:${PYTHONPATH}" \
        "${PORTAGE_BIN_PATH}"/portageq has_version "${ROOT}" "$1"
        local retval=$?
        case "${retval}" in
@@ -169,6 +170,7 @@ portageq() {
        if [ "${EBUILD_PHASE}" == "depend" ]; then
                die "portageq calls are not allowed in the global scope"
        fi
+       PYTHONPATH="${PORTAGE_PYM_PATH}:${PYTHONPATH}" \
        "${PORTAGE_BIN_PATH}/portageq" "$@"
 }
 
@@ -184,6 +186,7 @@ best_version() {
        fi
        # returns the best/most-current match.
        # Takes single depend-type atoms.
+       PYTHONPATH="${PORTAGE_PYM_PATH}:${PYTHONPATH}" \
        "${PORTAGE_BIN_PATH}/portageq" 'best_version' "${ROOT}" "$1"
 }
 
@@ -1363,7 +1366,7 @@ READONLY_EBUILD_METADATA="DEPEND DESCRIPTION
        EAPI HOMEPAGE INHERITED IUSE KEYWORDS LICENSE
        PDEPEND PROVIDE RDEPEND RESTRICT SLOT SRC_URI"
 
-READONLY_PORTAGE_VARS="A CATEGORY D EBUILD EBUILD_PHASE \
+READONLY_PORTAGE_VARS="A CATEGORY D EBUILD EBUILD_ENV_FILE EBUILD_PHASE \
        EBUILD_SH_ARGS EMERGE_FROM FILESDIR P PF PN \
        PORTAGE_BIN_PATH PORTAGE_PYM_PATH PORTAGE_MUTABLE_FILTERED_VARS \
        PORTAGE_TMPDIR PR PV PVR T WORKDIR ED"
@@ -1565,32 +1568,14 @@ if hasq "depend" "${EBUILD_SH_ARGS}"; then
        unset BIN_PATH BIN BODY FUNC_SRC
 fi
 
-# Automatically try to load environment.bz2 whenever
-# "${T}/environment" does not exist, except for phases
-# such as nofetch that do not require ${T} to exist.
-if ! hasq ${EBUILD_SH_ARGS} clean depend nofetch && \
-       [ ! -f "${T}/environment" ] ; then
-       bzip2 -dc "${EBUILD%/*}"/environment.bz2 > \
-               "${T}/environment" 2> /dev/null
-       if [ $? -eq 0 ] && [ -s "${T}/environment" ] ; then
-               preprocess_ebuild_env || \
-                       die "error processing '${EBUILD%/*}/environment.bz2'"
-       else
-               rm -f "${T}/environment"
-       fi
-fi
-
 if hasq ${EBUILD_SH_ARGS} clean ; then
        true
 elif ! hasq ${EBUILD_PHASE} depend && [ -f "${T}"/environment ] ; then
-       if [ "${PN}" == "portage" ] && [ -n "${EBUILD_SH_ARGS}" ] ; then
-               # When portage reinstalls itself, during inst/rm phases, the
-               # environment may have been saved by a different version of ebuild.sh,
-               # so it can't trusted that it's been properly filtered. Therefore,
-               # always preprocess the environment when ${PN} == portage.
-               preprocess_ebuild_env || \
-                       die "error processing environment"
-       fi
+       # The environment may have been extracted from environment.bz2 or
+       # may have come from another version of ebuild.sh or something.
+       # In any case, preprocess it to prevent any potential interference.
+       preprocess_ebuild_env || \
+               die "error processing environment"
        # Colon separated SANDBOX_* variables need to be cumulative.
        for x in SANDBOX_DENY SANDBOX_READ SANDBOX_PREDICT SANDBOX_WRITE ; do
                eval PORTAGE_${x}=\${!x}
index 68075a3992856b83e356936c15d52635d765ff25..c08044cbf990890de5c9953943ddd9928b825fa8 100755 (executable)
@@ -425,7 +425,7 @@ save_ebuild_env() {
                        EBUILD_EXIT_STATUS_FILE EBUILD_MASTER_PID \
                        ECLASSDIR ECLASS_DEPTH ENDCOL FAKEROOTKEY FEATURES \
                        GOOD HILITE HOME IMAGE \
-                       KV LAST_E_CMD LAST_E_LEN LD_PRELOAD MOPREFIX \
+                       KV LAST_E_CMD LAST_E_LEN LD_PRELOAD MISC_FUNCTIONS_ARGS MOPREFIX \
                        NORMAL PATH PKGDIR PKGUSE PKG_LOGDIR PKG_TMPDIR \
                        PORTAGE_ACTUAL_DISTDIR PORTAGE_ARCHLIST PORTAGE_BASHRC \
                        PORTAGE_BINPKG_TMPFILE PORTAGE_BUILDDIR \
index 1c44fce59ab48efa318f3b21993098a066cc631f..cd1ee4a2d7c1b71d204289b961ff0f9d0553df69 100644 (file)
@@ -863,6 +863,17 @@ class config(object):
        virtuals ...etc you look in here.
        """
 
+       # Preserve backupenv values that are initialized in the config
+       # constructor. Also, preserve XARGS since it is set by the
+       # portage.data module.
+       _environ_whitelist = frozenset([
+               "FEATURES", "PORTAGE_BIN_PATH",
+               "PORTAGE_CONFIGROOT", "PORTAGE_DEPCACHEDIR",
+               "PORTAGE_GID", "PORTAGE_INST_GID", "PORTAGE_INST_UID",
+               "PORTAGE_PYM_PATH", "PORTDIR_OVERLAY", "ROOT", "USE_ORDER",
+               "XARGS",
+       ])
+
        # Filter selected variables in the config.environ() method so that
        # they don't needlessly propagate down into the ebuild environment.
        _environ_filter = []
@@ -926,6 +937,7 @@ class config(object):
 
                self.already_in_regenerate = 0
 
+               self._filter_calling_env = False
                self.locked   = 0
                self.mycpv    = None
                self.puse     = []
@@ -951,6 +963,7 @@ class config(object):
                self._use_wildcards = False
 
                if clone:
+                       self._filter_calling_env = copy.deepcopy(clone._filter_calling_env)
                        self.incrementals = copy.deepcopy(clone.incrementals)
                        self.profile_path = copy.deepcopy(clone.profile_path)
                        self.user_profile_dir = copy.deepcopy(clone.user_profile_dir)
@@ -2544,6 +2557,9 @@ class config(object):
                "return our locally-maintained environment"
                mydict={}
                environ_filter = self._environ_filter
+               filter_calling_env = self._filter_calling_env
+               environ_whitelist = self._environ_whitelist
+               env_d = self.configdict["env.d"]
                for x in self:
                        if x in environ_filter:
                                continue
@@ -2552,6 +2568,11 @@ class config(object):
                                writemsg("!!! Non-string value in config: %s=%s\n" % \
                                        (x, myvalue), noiselevel=-1)
                                continue
+                       if filter_calling_env and \
+                               x not in environ_whitelist:
+                               if myvalue == env_d.get(x) or \
+                                       myvalue == os.environ.get(x):
+                                       continue
                        mydict[x] = myvalue
                if not mydict.has_key("HOME") and mydict.has_key("BUILD_PREFIX"):
                        writemsg("*** HOME not set. Setting to "+mydict["BUILD_PREFIX"]+"\n")
@@ -3594,8 +3615,15 @@ def spawnebuild(mydo,actionmap,mysettings,debug,alwaysdep=0,logfile=None):
        mysettings["EBUILD_PHASE"] = mydo
        _doebuild_exit_status_unlink(
                mysettings.get("EBUILD_EXIT_STATUS_FILE"))
-       phase_retval = spawn(actionmap[mydo]["cmd"] % mydo, mysettings, debug=debug, logfile=logfile, **kwargs)
-       mysettings["EBUILD_PHASE"] = ""
+       filter_calling_env_state = mysettings._filter_calling_env
+       if os.path.exists(os.path.join(mysettings["T"], "environment")):
+               mysettings._filter_calling_env = True
+       try:
+               phase_retval = spawn(actionmap[mydo]["cmd"] % mydo,
+                       mysettings, debug=debug, logfile=logfile, **kwargs)
+       finally:
+               mysettings["EBUILD_PHASE"] = ""
+               mysettings._filter_calling_env = filter_calling_env_state
        msg = _doebuild_exit_status_check(mydo, mysettings)
        if msg:
                phase_retval = 1
@@ -3636,7 +3664,13 @@ def spawnebuild(mydo,actionmap,mysettings,debug,alwaysdep=0,logfile=None):
                                        apply_secpass_permissions(fpath, uid=myuid, gid=mygid,
                                                mode=mystat.st_mode, stat_cached=mystat,
                                                follow_links=False)
-                       mycommand = " ".join([MISC_SH_BINARY, "install_qa_check", "install_symlink_html_docs"])
+                       # Note: PORTAGE_BIN_PATH may differ from the global
+                       # constant when portage is reinstalling itself.
+                       portage_bin_path = mysettings["PORTAGE_BIN_PATH"]
+                       misc_sh_binary = os.path.join(portage_bin_path,
+                               os.path.basename(MISC_SH_BINARY))
+                       mycommand = " ".join([misc_sh_binary,
+                               "install_qa_check", "install_symlink_html_docs"])
                        qa_retval = spawn(mycommand, mysettings, debug=debug, logfile=logfile, **kwargs)
                        if qa_retval:
                                writemsg("!!! install_qa_check failed; exiting.\n",
@@ -3786,8 +3820,11 @@ def doebuild_environment(myebuild, mydo, myroot, mysettings, debug, use_cache, m
                mysplit=mysettings["PATH"].split(":")
        else:
                mysplit=[]
-       if PORTAGE_BIN_PATH not in mysplit:
-               mysettings["PATH"]=PORTAGE_BIN_PATH+":"+mysettings["PATH"]
+       # Note: PORTAGE_BIN_PATH may differ from the global constant
+       # when portage is reinstalling itself.
+       portage_bin_path = mysettings["PORTAGE_BIN_PATH"]
+       if portage_bin_path not in mysplit:
+               mysettings["PATH"] = portage_bin_path + ":" + mysettings["PATH"]
 
        # Sandbox needs cannonical paths.
        mysettings["PORTAGE_TMPDIR"] = os.path.realpath(
@@ -4024,12 +4061,6 @@ def _doebuild_exit_status_check(mydo, settings):
        Returns an error string if the shell appeared
        to exit unsuccessfully, None otherwise.
        """
-       if settings.get("ROOT") == "/" and \
-               settings.get("PN") == "portage":
-                       # portage upgrade or downgrade invalidates this check
-                       # since ebuild.sh portage version may differ from the
-                       # current instance that is running in python.
-                       return None
        exit_status_file = settings.get("EBUILD_EXIT_STATUS_FILE")
        if not exit_status_file or \
                os.path.exists(exit_status_file):
@@ -4211,10 +4242,19 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0,
                                eerror(l, phase=mydo, key=mysettings.mycpv)
                return retval
 
+       # Note: PORTAGE_BIN_PATH may differ from the global
+       # constant when portage is reinstalling itself.
+       portage_bin_path = mysettings["PORTAGE_BIN_PATH"]
+       ebuild_sh_binary = os.path.join(portage_bin_path,
+               os.path.basename(EBUILD_SH_BINARY))
+       misc_sh_binary = os.path.join(portage_bin_path,
+               os.path.basename(MISC_SH_BINARY))
+
        logfile=None
        builddir_lock = None
        tmpdir = None
        tmpdir_orig = None
+       filter_calling_env_state = mysettings._filter_calling_env
        try:
                if mydo in ("digest", "manifest", "help"):
                        # Temporarily exempt the depend phase from manifest checks, in case
@@ -4242,7 +4282,7 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0,
                                mysettings["dbkey"] = ""
                                pr, pw = os.pipe()
                                fd_pipes = {0:0, 1:1, 2:2, 9:pw}
-                               mypids = spawn(EBUILD_SH_BINARY + " depend", mysettings,
+                               mypids = spawn(ebuild_sh_binary + " depend", mysettings,
                                        fd_pipes=fd_pipes, returnpid=True, droppriv=droppriv)
                                os.close(pw) # belongs exclusively to the child process now
                                maxbytes = 1024
@@ -4271,7 +4311,7 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0,
                                mysettings["dbkey"] = \
                                        os.path.join(mysettings.depcachedir, "aux_db_key_temp")
 
-                       return spawn(EBUILD_SH_BINARY + " depend", mysettings,
+                       return spawn(ebuild_sh_binary + " depend", mysettings,
                                droppriv=droppriv)
 
                # Validate dependency metadata here to ensure that ebuilds with invalid
@@ -4337,6 +4377,46 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0,
                        if logfile and not os.access(os.path.dirname(logfile), os.W_OK):
                                logfile = None
                if have_build_dirs:
+                       env_file = os.path.join(mysettings["T"], "environment")
+                       env_stat = None
+                       saved_env = None
+                       try:
+                               env_stat = os.stat(env_file)
+                       except OSError, e:
+                               if e.errno != errno.ENOENT:
+                                       raise
+                               del e
+                       if not env_stat:
+                               saved_env = os.path.join(
+                                       os.path.dirname(myebuild), "environment.bz2")
+                               if not os.path.isfile(saved_env):
+                                       saved_env = None
+                       if saved_env:
+                               retval = os.system(
+                                       "bzip2 -dc '%s' > '%s'" % (saved_env, env_file))
+                               try:
+                                       env_stat = os.stat(env_file)
+                               except OSError, e:
+                                       if e.errno != errno.ENOENT:
+                                               raise
+                                       del e
+                               if os.WIFEXITED(retval) and \
+                                       os.WEXITSTATUS(retval) == os.EX_OK and \
+                                       env_stat and env_stat.st_size > 0:
+                                       pass
+                               else:
+                                       writemsg("!!! Error extracting saved environment: '%s'" % \
+                                               saved_env, noiselevel=-1)
+                                       try:
+                                               os.unlink(env_file)
+                                       except OSError, e:
+                                               if e.errno != errno.ENOENT:
+                                                       raise
+                                               del e
+                                       env_stat = None
+                       if env_stat:
+                               mysettings._filter_calling_env = True
+                       del env_file, env_stat, saved_env
                        _doebuild_exit_status_unlink(
                                mysettings.get("EBUILD_EXIT_STATUS_FILE"))
                else:
@@ -4345,10 +4425,10 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0,
                # if any of these are being called, handle them -- running them out of
                # the sandbox -- and stop now.
                if mydo in ["clean","cleanrm"]:
-                       return spawn(EBUILD_SH_BINARY + " clean", mysettings,
+                       return spawn(ebuild_sh_binary + " clean", mysettings,
                                debug=debug, free=1, logfile=None)
                elif mydo == "help":
-                       return spawn(EBUILD_SH_BINARY + " " + mydo, mysettings,
+                       return spawn(ebuild_sh_binary + " " + mydo, mysettings,
                                debug=debug, free=1, logfile=logfile)
                elif mydo == "setup":
                        infodir = os.path.join(
@@ -4357,7 +4437,7 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0,
                                """Load USE flags for setup phase of a binary package.
                                Ideally, the environment.bz2 would be used instead."""
                                mysettings.load_infodir(infodir)
-                       retval = spawn(EBUILD_SH_BINARY + " " + mydo, mysettings,
+                       retval = spawn(ebuild_sh_binary + " " + mydo, mysettings,
                                debug=debug, free=1, logfile=logfile)
                        retval = exit_status_check(retval)
                        if secpass >= 2:
@@ -4368,13 +4448,13 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0,
                                        filemode=060, filemask=0)
                        return retval
                elif mydo == "preinst":
-                       phase_retval = spawn(" ".join((EBUILD_SH_BINARY, mydo)),
+                       phase_retval = spawn(" ".join((ebuild_sh_binary, mydo)),
                                mysettings, debug=debug, free=1, logfile=logfile)
                        phase_retval = exit_status_check(phase_retval)
                        if phase_retval == os.EX_OK:
                                # Post phase logic and tasks that have been factored out of
                                # ebuild.sh.
-                               myargs = [MISC_SH_BINARY, "preinst_bsdflags", "preinst_mask",
+                               myargs = [misc_sh_binary, "preinst_bsdflags", "preinst_mask",
                                        "preinst_sfperms", "preinst_selinux_labels",
                                        "preinst_suid_scan"]
                                _doebuild_exit_status_unlink(
@@ -4389,13 +4469,13 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0,
                        return phase_retval
                elif mydo == "postinst":
                        mysettings.load_infodir(mysettings["O"])
-                       phase_retval = spawn(" ".join((EBUILD_SH_BINARY, mydo)),
+                       phase_retval = spawn(" ".join((ebuild_sh_binary, mydo)),
                                mysettings, debug=debug, free=1, logfile=logfile)
                        phase_retval = exit_status_check(phase_retval)
                        if phase_retval == os.EX_OK:
                                # Post phase logic and tasks that have been factored out of
                                # ebuild.sh.
-                               myargs = [MISC_SH_BINARY, "postinst_bsdflags"]
+                               myargs = [misc_sh_binary, "postinst_bsdflags"]
                                _doebuild_exit_status_unlink(
                                        mysettings.get("EBUILD_EXIT_STATUS_FILE"))
                                mysettings["EBUILD_PHASE"] = ""
@@ -4408,7 +4488,7 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0,
                        return phase_retval
                elif mydo in ("prerm", "postrm", "config", "info"):
                        mysettings.load_infodir(mysettings["O"])
-                       retval =  spawn(EBUILD_SH_BINARY + " " + mydo,
+                       retval =  spawn(ebuild_sh_binary + " " + mydo,
                                mysettings, debug=debug, free=1, logfile=logfile)
                        retval = exit_status_check(retval)
                        return retval
@@ -4464,11 +4544,9 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0,
                                # mod_echo module might push the original message off of the
                                # top of the terminal and prevent the user from being able to
                                # see it.
-                               mysettings["EBUILD_PHASE"] = "unpack"
-                               cmd = "source '%s/isolated-functions.sh' ; " % PORTAGE_BIN_PATH
-                               cmd += "eerror \"Fetch failed for '%s'\"" % mycpv
-                               portage.process.spawn(["bash", "-c", cmd],
-                                       env=mysettings.environ())
+                               from portage.elog.messages import eerror
+                               eerror("Fetch failed for '%s'" % mycpv,
+                                       phase="unpack", key=mycpv)
                                from portage.elog import elog_process
                                elog_process(mysettings.mycpv, mysettings)
                        return 1
@@ -4546,8 +4624,8 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0,
 
                fakeroot = "fakeroot" in mysettings.features
 
-               ebuild_sh = EBUILD_SH_BINARY + " %s"
-               misc_sh = MISC_SH_BINARY + " dyn_%s"
+               ebuild_sh = ebuild_sh_binary + " %s"
+               misc_sh = misc_sh_binary + " dyn_%s"
 
                # args are for the to spawn function
                actionmap = {
@@ -4609,6 +4687,7 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0,
                return retval
 
        finally:
+               mysettings._filter_calling_env = filter_calling_env_state
                if tmpdir:
                        mysettings["PORTAGE_TMPDIR"] = tmpdir_orig
                        shutil.rmtree(tmpdir)
@@ -6004,15 +6083,11 @@ def create_trees(config_root=None, target_root=None, trees=None):
                # with ROOT != "/", so we wipe out the "backupenv" for the
                # config that is associated with ROOT == "/" and regenerate
                # it's incrementals.
-
                # Preserve backupenv values that are initialized in the config
                # constructor. Also, preserve XARGS since it is set by the
                # portage.data module.
-               backupenv_whitelist = set(["FEATURES", "PORTAGE_BIN_PATH",
-                       "PORTAGE_CONFIGROOT", "PORTAGE_DEPCACHEDIR",
-                       "PORTAGE_GID", "PORTAGE_INST_GID", "PORTAGE_INST_UID",
-                       "PORTAGE_PYM_PATH", "PORTDIR_OVERLAY", "ROOT", "USE_ORDER",
-                       "XARGS"])
+
+               backupenv_whitelist = settings._environ_whitelist
                backupenv = settings.configdict["backupenv"]
                for k, v in os.environ.iteritems():
                        if k in backupenv_whitelist:
index 61083147479ba450a177dd63801ed955936a2c4d..1fc9b213d3176b3767dc111e5b70dc391a46a073 100644 (file)
@@ -2004,8 +2004,18 @@ class dblink(object):
                outfile.flush()
                outfile.close()
 
+               # If portage is reinstalling itself, remove the old
+               # version now since we want to use the temporary
+               # PORTAGE_BIN_PATH that will be removed when we return.
+               reinstall_self = False
+               if self.myroot == "/" and \
+                       "sys-apps" == self.cat and \
+                       "portage" == pkgsplit(self.pkg)[0]:
+                       reinstall_self = True
+
                for dblnk in others_in_slot:
-                       if dblnk.mycpv != self.mycpv:
+                       if dblnk.mycpv != self.mycpv and \
+                               not reinstall_self:
                                continue
                        writemsg_stdout(">>> Safely unmerging already-installed instance...\n")
                        # These caches are populated during collision-protect and the data
@@ -2019,8 +2029,11 @@ class dblink(object):
                        others_in_slot.remove(dblnk) # dblnk will unmerge itself now
                        dblnk.unmerge(trimworld=0, ldpath_mtimes=prev_mtimes,
                                others_in_slot=others_in_slot)
+                       # TODO: Check status and abort if necessary.
+                       dblnk.delete()
                        writemsg_stdout(">>> Original instance of package unmerged safely.\n")
-                       break
+                       if not reinstall_self:
+                               break
 
                # We hold both directory locks.
                self.dbdir = self.dbpkgdir
@@ -2336,6 +2349,45 @@ class dblink(object):
                                writemsg_stdout(zing + " " + mydest + "\n")
 
        def merge(self, mergeroot, inforoot, myroot, myebuild=None, cleanup=0,
+               mydbapi=None, prev_mtimes=None):
+               """
+               If portage is reinstalling itself, create temporary
+               copies of PORTAGE_BIN_PATH and PORTAGE_PYM_PATH in order
+               to avoid relying on the new versions which may be
+               incompatible. Register an atexit hook to clean up the
+               temporary directories. Pre-load elog modules here since
+               we won't be able to later if they get unmerged (happens
+               when namespace changes).
+               """
+               if self.myroot == "/" and \
+                       "sys-apps" == self.cat and \
+                       "portage" == pkgsplit(self.pkg)[0]:
+                       settings = self.settings
+                       base_path_orig = os.path.dirname(settings["PORTAGE_BIN_PATH"])
+                       from tempfile import mkdtemp
+                       import shutil
+                       base_path_tmp = mkdtemp()
+                       from portage.process import atexit_register
+                       atexit_register(shutil.rmtree, base_path_tmp)
+                       dir_perms = 0755
+                       for subdir in "bin", "pym":
+                               var_name = "PORTAGE_%s_PATH" % subdir.upper()
+                               var_orig = settings[var_name]
+                               var_new = os.path.join(base_path_tmp, subdir)
+                               settings[var_name] = var_new
+                               settings.backup_changes(var_name)
+                               shutil.copytree(var_orig, var_new, symlinks=True)
+                               os.chmod(var_new, dir_perms)
+                       os.chmod(base_path_tmp, dir_perms)
+                       # This serves so pre-load the modules.
+                       elog_process(self.mycpv, self.settings,
+                               phasefilter=filter_mergephases)
+
+               return self._merge(mergeroot, inforoot,
+                               myroot, myebuild=myebuild, cleanup=cleanup,
+                               mydbapi=mydbapi, prev_mtimes=prev_mtimes)
+
+       def _merge(self, mergeroot, inforoot, myroot, myebuild=None, cleanup=0,
                mydbapi=None, prev_mtimes=None):
                retval = -1
                self.lockdb()
index e1df6d9bd3c2f0d8ac8630316a6d89304f7e1a97..d3e333534384ef693a6d77e1eb48741137e5c66b 100644 (file)
@@ -42,9 +42,31 @@ def _combine_logentries(logentries):
                        rValue.append("\n")
        return "".join(rValue)
 
+_elog_mod_imports = {}
 _elog_atexit_handlers = []
 _preserve_logentries = {}
 def elog_process(cpv, mysettings, phasefilter=None):
+
+       global _elog_mod_imports
+       logsystems = mysettings.get("PORTAGE_ELOG_SYSTEM","").split()
+       for s in logsystems:
+               # allow per module overrides of PORTAGE_ELOG_CLASSES
+               if ":" in s:
+                       s, levels = s.split(":", 1)
+                       levels = levels.split(",")
+               # - is nicer than _ for module names, so allow people to use it.
+               s = s.replace("-", "_")
+               try:
+                       name = "portage.elog.mod_" + s
+                       m = _elog_mod_imports.get(name)
+                       if m is None:
+                               m = __import__(name)
+                               for comp in name.split(".")[1:]:
+                                       m = getattr(m, comp)
+                               _elog_mod_imports[name] = m
+               except ImportError:
+                       pass
+
        ebuild_logentries = collect_ebuild_messages(os.path.join(mysettings["T"], "logging"))
        all_logentries = collect_messages()
        if all_logentries.has_key(cpv):
@@ -94,9 +116,12 @@ def elog_process(cpv, mysettings, phasefilter=None):
                                # FIXME: ugly ad.hoc import code
                                # TODO:  implement a common portage module loader
                                name = "portage.elog.mod_" + s
-                               m = __import__(name)
-                               for comp in name.split(".")[1:]:
-                                       m = getattr(m, comp)
+                               m = _elog_mod_imports.get(name)
+                               if m is None:
+                                       m = __import__(name)
+                                       for comp in name.split(".")[1:]:
+                                               m = getattr(m, comp)
+                                       _elog_mod_imports[name] = m
                                def timeout_handler(signum, frame):
                                        raise PortageException("Timeout in elog_process for system '%s'" % s)
                                import signal