Merged from trunk 8696:8729
authorFabian Groffen <grobian@gentoo.org>
Sun, 2 Dec 2007 21:28:02 +0000 (21:28 -0000)
committerFabian Groffen <grobian@gentoo.org>
Sun, 2 Dec 2007 21:28:02 +0000 (21:28 -0000)
   | 8698    | Make doebuild() use EBUILD_EXIT_STATUS_FILE sanity checks    |
   | zmedico | for the extra misc-functions.sh preinst/postinst shell code. |

   | 8700    | Make the environment handling code detect errors and die if  |
   | zmedico | necessary.                                                   |

   | 8702    | Bug #200231 - Make dir_get_metadata() handle an exception    |
   | zmedico | thrown from dir_get_list() when there's a problem connection |
   |         | problem.                                                     |

   | 8704    | Fix config.pop() so that is will properly raise a KeyError.  |
   | zmedico |                                                              |

   | 8706    | Don't forget to unlink ${EBUILD_EXIT_STATUS_FILE} before     |
   | zmedico | each phase.                                                  |

   | 8708    | Bug #200229 - The gimp ebuild defines AA as a local          |
   | zmedico | variable. In order to accomodate this, create a              |
   |         | PORTAGE_MUTABLE_FILTERED_VARS list for variables that        |
   |         | portage sets but doesn't mark readonly. In order to prevent  |
   |         | changed values from causing unexpcted interference, they are |
   |         | filtered out of the environment when it is saved or loaded   |
   |         | (any mutations do not persist).                              |

   | 8710    | Fix logic wrt exit_status_check and elog_process in          |
   | zmedico | doebuild().                                                  |

   | 8712    | Remove a redundant os.EX_OK check that exit_status_check()   |
   | zmedico | already handles internally.                                  |

   | 8714    | portage upgrade or downgrade invalidates                     |
   | zmedico | EBUILD_EXIT_STATUS_FILE sanity checks since ebuild.sh        |
   |         | portage version may differ from the current instance that is |
   |         | running in python.                                           |

   | 8716    | Just use ${PN} instead of catpkgsplit().                     |
   | zmedico |                                                              |

   | 8718    | In the preprocess_ebuild_env() subshell, check for errors    |
   | zmedico | and return early when necessary.                             |

   | 8720    | Add a sanity check in dblink.treewalk() so that a broken     |
   | zmedico | ebuild that doesn't install any files will not be able to    |
   |         | replace a package in the same slot that really installs      |
   |         | files. This check can be bypassed by manually unmerging the  |
   |         | old package or by setting PORTAGE_PACKAGE_EMPTY_ABORT="0" in |
   |         | /etc/make.conf.                                              |

   | 8721    | Don't export PORTAGE_PACKAGE_EMPTY_ABORT to the ebuild       |
   | zmedico | environment.                                                 |

   | 8724    | Disable PORTAGE_PACKAGE_EMPTY_ABORT by default since the     |
   | zmedico | scrollkeeper-9999 upgrade will trigger a false positive.     |
   |         | Thanks to leio for reporting.                                |

   | 8726    | In chk_updated_cfg_files(), avoid scanning for config files  |
   | zmedico | for paths that aren't writable by the current user. This     |
   |         | prevents Permission denied errors from being triggered later |
   |         | when trying to scan subdirectories that are inaccessible.    |

   | 8729    | Bug #200652 - Skip ${T}/environment handling for             |
   | zmedico | pkg_nofetch().                                               |

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

bin/ebuild.sh
pym/_emerge/__init__.py
pym/portage/__init__.py
pym/portage/dbapi/vartree.py
pym/portage/getbinpkg.py

index e24bcc9491cae5a8c1c52dff6a8f2bb7eabffeab..317507d3b230e7eb833402097136d8f820713a17 100755 (executable)
@@ -1363,9 +1363,16 @@ READONLY_EBUILD_METADATA="DEPEND DESCRIPTION
        EAPI HOMEPAGE INHERITED IUSE KEYWORDS LICENSE
        PDEPEND PROVIDE RDEPEND RESTRICT SLOT SRC_URI"
 
-READONLY_PORTAGE_VARS="D EBUILD EBUILD_PHASE ED \
-       EBUILD_SH_ARGS EMERGE_FROM FILESDIR PORTAGE_BIN_PATH \
-       PORTAGE_PYM_PATH PORTAGE_TMPDIR T WORKDIR"
+READONLY_PORTAGE_VARS="A CATEGORY D EBUILD 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"
+
+# Variables that portage sets but doesn't mark readonly.
+# In order to prevent changed values from causing unexpected
+# interference, they are filtered out of the environment when
+# it is saved or loaded (any mutations do not persist).
+PORTAGE_MUTABLE_FILTERED_VARS="AA"
 
 # @FUNCTION: filter_readonly_variables
 # @DESCRIPTION: [--filter-sandbox]
@@ -1388,7 +1395,7 @@ filter_readonly_variables() {
                SANDBOX_DEBUG_LOG SANDBOX_DISABLED SANDBOX_LIB
                SANDBOX_LOG"
        filtered_vars="${readonly_bash_vars} ${READONLY_PORTAGE_VARS}
-               BASH_[_[:alnum:]]*"
+               ${PORTAGE_MUTABLE_FILTERED_VARS} BASH_[_[:alnum:]]*"
        if hasq --filter-sandbox $* ; then
                filtered_vars="${filtered_vars} SANDBOX_[_[:alnum:]]*"
        else
@@ -1420,24 +1427,38 @@ filter_readonly_variables() {
 preprocess_ebuild_env() {
        filter_readonly_variables --filter-sandbox < "${T}"/environment \
                > "${T}"/environment.filtered
-       mv "${T}"/environment.filtered "${T}"/environment
+       if [ $? -ne 0 ] ; then
+               rm -f "${T}/environment.filtered"
+               return 1
+       fi
+       mv "${T}"/environment.filtered "${T}"/environment || return $?
+       rm -f "${T}/environment.success" || return $?
        # WARNING: Code inside this subshell should avoid making assumptions
        # about variables or functions after source "${T}"/environment has been
        # called. Any variables that need to be relied upon should already be
        # filtered out above.
        (
-               source "${T}"/environment
+               source "${T}/environment" || exit $?
 
                # It's remotely possible that save_ebuild_env() has been overridden
                # by the above source command. To protect ourselves, we override it
                # here with our own version. ${PORTAGE_BIN_PATH} is safe to use here
                # because it's already filtered above.
-               source "${PORTAGE_BIN_PATH}/isolated-functions.sh"
+               source "${PORTAGE_BIN_PATH}/isolated-functions.sh" || exit $?
 
                # Rely on save_ebuild_env() to filter out any remaining variables
                # and functions that could interfere with the current environment.
-               save_ebuild_env
-       ) | filter_readonly_variables > "${T}"/environment
+               save_ebuild_env || exit $?
+               touch "${T}/environment.success" || exit $?
+       ) | filter_readonly_variables > "${T}/environment.filtered"
+       if [ -e "${T}/environment.success" ] ; then
+               rm "${T}/environment.success"
+               mv "${T}/environment.filtered" "${T}/environment"
+               return $?
+       else
+               rm -f "${T}/environment.filtered"
+       fi
+       return 1
 }
 
 # === === === === === === === === === === === === === === === === === ===
@@ -1545,13 +1566,15 @@ if hasq "depend" "${EBUILD_SH_ARGS}"; then
 fi
 
 # Automatically try to load environment.bz2 whenever
-# "${T}/environment" does not exist.
-if ! hasq ${EBUILD_SH_ARGS} clean depend && \
+# "${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 [ -s "${T}/environment" ] ; then
-               preprocess_ebuild_env
+       if [ $? -eq 0 ] && [ -s "${T}/environment" ] ; then
+               preprocess_ebuild_env || \
+                       die "error processing '${EBUILD%/*}/environment.bz2'"
        else
                rm -f "${T}/environment"
        fi
@@ -1565,13 +1588,15 @@ elif ! hasq ${EBUILD_PHASE} depend && [ -f "${T}"/environment ] ; then
                # 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
+               preprocess_ebuild_env || \
+                       die "error processing environment"
        fi
        # Colon separated SANDBOX_* variables need to be cumulative.
        for x in SANDBOX_DENY SANDBOX_READ SANDBOX_PREDICT SANDBOX_WRITE ; do
                eval PORTAGE_${x}=\${!x}
        done
-       source "${T}"/environment
+       source "${T}"/environment || \
+               die "error sourcing environment"
        for x in SANDBOX_DENY SANDBOX_PREDICT SANDBOX_READ SANDBOX_WRITE ; do
                eval y=\${PORTAGE_${x}}
                if [ "${y}" != "${!x}" ] ; then
@@ -1663,10 +1688,6 @@ export TMPDIR="${T}"
 # declare them only after it has already run.
 if [ "${EBUILD_PHASE}" != "depend" ] ; then
        declare -r ${READONLY_EBUILD_METADATA} ${READONLY_PORTAGE_VARS}
-       for x in A AA CATEGORY EMERGE_FROM P PF PN PR PV PVR ; do
-               [[ ${!x-UNSET_VAR} != UNSET_VAR ]] && declare -r ${x}
-       done
-       unset x
 fi
 
 if [ -n "${EBUILD_SH_ARGS}" ] ; then
@@ -1767,7 +1788,7 @@ fi
 
 # Save the env only for relevant phases.
 if [ -n "${EBUILD_SH_ARGS}" ] && \
-       ! hasq ${EBUILD_SH_ARGS} clean help info; then
+       ! hasq ${EBUILD_SH_ARGS} clean help info nofetch ; then
        # Save current environment and touch a success file. (echo for success)
        umask 002
        save_ebuild_env | filter_readonly_variables > "${T}/environment"
index ad6febb0438a893852b263f84e21bff124004913..3133e9c56d267dbcb05be0c396617cf0ff849f41 100644 (file)
@@ -5080,6 +5080,10 @@ def chk_updated_cfg_files(target_root, config_protect):
                procount=0
                for x in config_protect:
                        x = os.path.join(target_root, x.lstrip(os.path.sep))
+                       if not os.access(x, os.W_OK):
+                               # Avoid Permission denied errors generated
+                               # later by `find`.
+                               continue
                        try:
                                mymode = os.lstat(x).st_mode
                        except OSError:
index a6fda28dd41e2091993fb75402e9a977deca5078..1c44fce59ab48efa318f3b21993098a066cc631f 100644 (file)
@@ -891,7 +891,8 @@ class config(object):
                "PORTAGE_ECLASS_WARNING_ENABLE", "PORTAGE_ELOG_CLASSES",
                "PORTAGE_ELOG_MAILFROM", "PORTAGE_ELOG_MAILSUBJECT",
                "PORTAGE_ELOG_MAILURI", "PORTAGE_ELOG_SYSTEM", "PORTAGE_GPG_DIR",
-               "PORTAGE_GPG_KEY", "PORTAGE_RSYNC_EXTRA_OPTS", "PORTAGE_RSYNC_OPTS",
+               "PORTAGE_GPG_KEY", "PORTAGE_PACKAGE_EMPTY_ABORT",
+               "PORTAGE_RSYNC_EXTRA_OPTS", "PORTAGE_RSYNC_OPTS",
                "PORTAGE_RSYNC_RETRIES", "PORT_LOGDIR", "QUICKPKG_DEFAULT_OPTS",
                "RESUMECOMMAND", "RESUMECOMMAND_HTTP", "RESUMECOMMAND_HTTP",
                "RESUMECOMMAND_SFTP", "SYNC", "USE_EXPAND_HIDDEN", "USE_ORDER",
@@ -2487,11 +2488,18 @@ class config(object):
                                return d[k]
                return x
 
-       def pop(self, k, x=None):
-               self.modifying()
-               v = x
+       def pop(self, key, *args):
+               if len(args) > 1:
+                       raise TypeError(
+                               "pop expected at most 2 arguments, got " + \
+                               repr(1 + len(args)))
+               v = self
                for d in reversed(self.lookuplist):
-                       v = d.pop(k, v)
+                       v = d.pop(key, v)
+               if v is self:
+                       if args:
+                               return args[0]
+                       raise KeyError(key)
                return v
 
        def has_key(self,mykey):
@@ -3588,8 +3596,7 @@ def spawnebuild(mydo,actionmap,mysettings,debug,alwaysdep=0,logfile=None):
                mysettings.get("EBUILD_EXIT_STATUS_FILE"))
        phase_retval = spawn(actionmap[mydo]["cmd"] % mydo, mysettings, debug=debug, logfile=logfile, **kwargs)
        mysettings["EBUILD_PHASE"] = ""
-       msg = _doebuild_exit_status_check(
-               mydo, mysettings.get("EBUILD_EXIT_STATUS_FILE"))
+       msg = _doebuild_exit_status_check(mydo, mysettings)
        if msg:
                phase_retval = 1
                from textwrap import wrap
@@ -4012,11 +4019,18 @@ def prepare_build_dirs(myroot, mysettings, cleanup):
                        mysettings["PORTAGE_LOG_FILE"] = os.path.join(
                                mysettings["T"], "build.log")
 
-def _doebuild_exit_status_check(mydo, exit_status_file):
+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):
                return None
@@ -4188,8 +4202,7 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0,
        def exit_status_check(retval):
                if retval != os.EX_OK:
                        return retval
-               msg = _doebuild_exit_status_check(
-                       mydo, mysettings.get("EBUILD_EXIT_STATUS_FILE"))
+               msg = _doebuild_exit_status_check(mydo, mysettings)
                if msg:
                        retval = 1
                        from textwrap import wrap
@@ -4364,9 +4377,12 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0,
                                myargs = [MISC_SH_BINARY, "preinst_bsdflags", "preinst_mask",
                                        "preinst_sfperms", "preinst_selinux_labels",
                                        "preinst_suid_scan"]
+                               _doebuild_exit_status_unlink(
+                                       mysettings.get("EBUILD_EXIT_STATUS_FILE"))
                                mysettings["EBUILD_PHASE"] = ""
                                phase_retval = spawn(" ".join(myargs),
                                        mysettings, debug=debug, free=1, logfile=logfile)
+                               phase_retval = exit_status_check(phase_retval)
                                if phase_retval != os.EX_OK:
                                        writemsg("!!! post preinst failed; exiting.\n",
                                                noiselevel=-1)
@@ -4380,9 +4396,12 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0,
                                # Post phase logic and tasks that have been factored out of
                                # ebuild.sh.
                                myargs = [MISC_SH_BINARY, "postinst_bsdflags"]
+                               _doebuild_exit_status_unlink(
+                                       mysettings.get("EBUILD_EXIT_STATUS_FILE"))
                                mysettings["EBUILD_PHASE"] = ""
                                phase_retval = spawn(" ".join(myargs),
                                        mysettings, debug=debug, free=1, logfile=logfile)
+                               phase_retval = exit_status_check(phase_retval)
                                if phase_retval != os.EX_OK:
                                        writemsg("!!! post postinst failed; exiting.\n",
                                                noiselevel=-1)
@@ -4570,9 +4589,8 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0,
                elif mydo=="merge":
                        retval = spawnebuild("install", actionmap, mysettings, debug,
                                alwaysdep=1, logfile=logfile)
-                       if retval == os.EX_OK:
-                               retval = exit_status_check(retval)
-                       else:
+                       retval = exit_status_check(retval)
+                       if retval != os.EX_OK:
                                # The merge phase handles this already.  Callers don't know how
                                # far this function got, so we have to call elog_process() here
                                # so that it's only called once.
index d6e14311020f20b336d8bc2622a1e8b8cc598548..61083147479ba450a177dd63801ed955936a2c4d 100644 (file)
@@ -1716,6 +1716,11 @@ class dblink(object):
                if slot is None:
                        slot = ""
 
+               from portage.elog.messages import eerror as _eerror
+               def eerror(lines):
+                       for l in lines:
+                               _eerror(l, phase="preinst", key=self.settings.mycpv)
+
                if slot != self.settings["SLOT"]:
                        writemsg("!!! WARNING: Expected SLOT='%s', got '%s'\n" % \
                                (self.settings["SLOT"], slot))
@@ -1781,6 +1786,42 @@ class dblink(object):
                                        # to an infinite recursion loop.
                                        mylinklist.append(file_path[len(srcroot):])
 
+               # If there are no files to merge, and an installed package in the same
+               # slot has files, it probably means that something went wrong.
+               if self.settings.get("PORTAGE_PACKAGE_EMPTY_ABORT") == "1" and \
+                       not myfilelist and not mylinklist and others_in_slot:
+                       installed_files = None
+                       for other_dblink in others_in_slot:
+                               installed_files = other_dblink.getcontents()
+                               if not installed_files:
+                                       continue
+                               from textwrap import wrap
+                               wrap_width = 72
+                               msg = []
+                               d = (
+                                       self.mycpv,
+                                       other_dblink.mycpv
+                               )
+                               msg.extend(wrap(("The '%s' package will not install " + \
+                                       "any files, but the currently installed '%s'" + \
+                                       " package has the following files: ") % d, wrap_width))
+                               msg.append("")
+                               msg.extend(sorted(installed_files))
+                               msg.append("")
+                               msg.append("package %s NOT merged" % self.mycpv)
+                               msg.append("")
+                               msg.extend(wrap(
+                                       ("Manually run `emerge --unmerge =%s` " % \
+                                       other_dblink.mycpv) + "if you really want to " + \
+                                       "remove the above files. Set " + \
+                                       "PORTAGE_PACKAGE_EMPTY_ABORT=\"0\" in " + \
+                                       "/etc/make.conf if you do not want to " + \
+                                       "abort in cases like this.",
+                                       wrap_width))
+                               eerror(msg)
+                       if installed_files:
+                               return 1
+
                # Preserve old libs if they are still in use
                if slot_matches and "preserve-libs" in self.settings.features:
                        self._preserve_libs(srcroot, destroot, myfilelist+mylinklist, counter)
@@ -1797,11 +1838,6 @@ class dblink(object):
                        self.settings, 0, 0, mydbapi)
                prepare_build_dirs(destroot, self.settings, cleanup)
 
-               from portage.elog.messages import eerror as _eerror
-               def eerror(lines):
-                       for l in lines:
-                               _eerror(l, phase="preinst", key=self.settings.mycpv)
-
                if collisions:
                        collision_protect = "collision-protect" in self.settings.features
                        msg = "This package will overwrite one or more files that" + \
index 6e6e60cc338efd803527512af839b93ae7bfe9bc..6c2d32cdb7f83b040557f14ffc4166fac801afcf 100644 (file)
@@ -308,7 +308,9 @@ def dir_get_list(baseurl,conn=None):
                        del page
                        listing = parser.get_anchors()
                else:
-                       raise Exception, "Unable to get listing: %s" % msg
+                       import portage.exception
+                       raise portage.exception.PortageException(
+                               "Unable to get listing: %s %s" % (rc,msg))
        elif protocol in ["ftp"]:
                if address[-1] == '/':
                        olddir = conn.pwd()
@@ -487,11 +489,13 @@ def dir_get_metadata(baseurl, conn=None, chunk_size=3000, verbose=1, usingcache=
                sys.stderr.write("!!! Permission denied: '%s'\n" % cache_path)
                return metadata[baseurl]["data"]
 
+       import portage.exception
        try:
                filelist = dir_get_list(baseurl, conn)
-       except Exception, e:
-               sys.stderr.write("!!! "+str(e)+"\n")
-               sys.stderr.flush()
+       except portage.exception.PortageException, e:
+               sys.stderr.write("!!! Error connecting to '%s'.\n" % baseurl)
+               sys.stderr.write("!!! %s\n" % str(e))
+               del e
                return metadata[baseurl]["data"]
        tbz2list = match_in_array(filelist, suffix=".tbz2")
        metalist = match_in_array(filelist, prefix="metadata.idx")