From 159f46435edd25c7dfd77bdc4c2f017e56f9d99d Mon Sep 17 00:00:00 2001 From: Fabian Groffen Date: Sun, 2 Dec 2007 21:28:02 +0000 Subject: [PATCH] Merged from trunk 8696:8729 | 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 | 61 ++++++++++++++++++++++++------------ pym/_emerge/__init__.py | 4 +++ pym/portage/__init__.py | 44 ++++++++++++++++++-------- pym/portage/dbapi/vartree.py | 46 ++++++++++++++++++++++++--- pym/portage/getbinpkg.py | 12 ++++--- 5 files changed, 125 insertions(+), 42 deletions(-) diff --git a/bin/ebuild.sh b/bin/ebuild.sh index e24bcc949..317507d3b 100755 --- a/bin/ebuild.sh +++ b/bin/ebuild.sh @@ -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" diff --git a/pym/_emerge/__init__.py b/pym/_emerge/__init__.py index ad6febb04..3133e9c56 100644 --- a/pym/_emerge/__init__.py +++ b/pym/_emerge/__init__.py @@ -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: diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py index a6fda28dd..1c44fce59 100644 --- a/pym/portage/__init__.py +++ b/pym/portage/__init__.py @@ -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. diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py index d6e143110..610831474 100644 --- a/pym/portage/dbapi/vartree.py +++ b/pym/portage/dbapi/vartree.py @@ -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" + \ diff --git a/pym/portage/getbinpkg.py b/pym/portage/getbinpkg.py index 6e6e60cc3..6c2d32cdb 100644 --- a/pym/portage/getbinpkg.py +++ b/pym/portage/getbinpkg.py @@ -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") -- 2.26.2