From 254129738ff31a23d49db642a39cff4c320af6e2 Mon Sep 17 00:00:00 2001 From: Fabian Groffen Date: Sun, 2 Dec 2007 21:19:10 +0000 Subject: [PATCH] Merged from trunk 8661:8696 | 8662 | Remove some unsafe assumptions from preprocess_ebuild_env() | | zmedico | and note the remaining assumption that save_ebuild_env() is | | | not overridden by the environment being loaded. | | 8663 | Make preprocess_ebuild_env() reload a known good version of | | zmedico | save_ebuild_env() in order to avoid making an assumption | | | that it hasn't been overridden. | | 8666 | Filter _RC_GET_KV_CACHE from the saved environment. | | zmedico | | | 8668 | Bug #124041 - Make emerge show an informative warning | | zmedico | message when one or more eclasses override eclasses from | | | PORTDIR. The warning can be permanently disabled by setting | | | PORTAGE_ECLASS_WARNING_ENABLE="0" in /etc/make.conf. | | 8670 | * Move filtering of a bunch of portage config variables from | | zmedico | save_ebuild_env() to config.environ(). * Add EMERGE_FROM to | | | the READONLY_PORTAGE_VARS list. | | 8672 | Add NOCONFMEM to config._environ_filter. | | zmedico | | | 8674 | unset ECLASS along with the other variables from inherit(). | | zmedico | | | 8676 | Filter more misc environment variables. | | zmedico | | | 8678 | Make diefunc() show the path of ${T}/environment since it's | | zmedico | usually in the stack trace and the user is likely to be | | | interested in it. | | 8680 | Automatically try to load environment.bz2 whenever | | zmedico | "${T}/environment" does not exist, except for "clean" and | | | "depend" phases. | | 8682 | Bug #200313 - Detect and report when an ebuild phase exits | | zmedico | unexpectedly. This is type of behavior is known to be | | | triggered by things such as failed variable assignments (bug | | | #190128) or bad substitution errors (bug #200313). We use a | | | EBUILD_EXIT_STATUS_FILE environment variable to specify a | | | file that the shell code is supposed to create when it exits | | | in a normal manner. If the file does not get created like | | | it's supposed to be then we can conclude that the shell has | | | exited in some unexpected way. | | 8683 | --buildpkgonly will not merge anything, so it cancels all | | zmedico | binary package options. | | 8686 | Check if ${EBUILD_EXIT_STATUS_FILE} is empty before | | zmedico | attempting to use it. | | 8688 | Implement config.pop(). | | zmedico | | | 8690 | Make config.pop() properly call config.modifying(). | | zmedico | | | 8692 | * In doebuild(), don't create directories for the "clean" or | | zmedico | "unmerge". * Fix dyn_clean() to clean the status file and | | | return early when appropriate. | | 8694 | Fix grammar. | | zmedico | | | 8696 | Make doebuild skip directory creation for the "cleanrm" | | zmedico | command. | svn path=/main/branches/prefix/; revision=8809 --- bin/ebuild.sh | 116 ++++++++------------------------ bin/isolated-functions.sh | 67 +++++++++++++++++++ bin/misc-functions.sh | 3 + pym/_emerge/__init__.py | 44 ++++++++++-- pym/portage/__init__.py | 130 ++++++++++++++++++++++++++++++++++-- pym/portage/eclass_cache.py | 10 ++- 6 files changed, 270 insertions(+), 100 deletions(-) diff --git a/bin/ebuild.sh b/bin/ebuild.sh index f8b33f698..e24bcc949 100755 --- a/bin/ebuild.sh +++ b/bin/ebuild.sh @@ -680,8 +680,9 @@ dyn_clean() { if [ -z "${PORTAGE_BUILDDIR}" ]; then echo "Aborting clean phase because PORTAGE_BUILDDIR is unset!" return 1 + elif [ ! -d "${PORTAGE_BUILDDIR}" ] ; then + return 0 fi - if type -P chflags > /dev/null ; then chflags -R noschg,nouchg,nosappnd,nouappnd "${PORTAGE_BUILDDIR}" chflags -R nosunlnk,nouunlnk "${PORTAGE_BUILDDIR}" 2>/dev/null @@ -696,6 +697,7 @@ dyn_clean() { fi if ! hasq keepwork $FEATURES; then + rm -rf "${PORTAGE_BUILDDIR}/.exit_status" rm -rf "${PORTAGE_BUILDDIR}/.logid" rm -rf "${PORTAGE_BUILDDIR}/.unpacked" rm -rf "${PORTAGE_BUILDDIR}/.compiled" @@ -1361,8 +1363,9 @@ 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 EBUILD_SH_ARGS FILESDIR \ - PORTAGE_BIN_PATH PORTAGE_PYM_PATH PORTAGE_TMPDIR T WORKDIR" +READONLY_PORTAGE_VARS="D EBUILD EBUILD_PHASE ED \ + EBUILD_SH_ARGS EMERGE_FROM FILESDIR PORTAGE_BIN_PATH \ + PORTAGE_PYM_PATH PORTAGE_TMPDIR T WORKDIR" # @FUNCTION: filter_readonly_variables # @DESCRIPTION: [--filter-sandbox] @@ -1418,91 +1421,23 @@ preprocess_ebuild_env() { filter_readonly_variables --filter-sandbox < "${T}"/environment \ > "${T}"/environment.filtered mv "${T}"/environment.filtered "${T}"/environment + # 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 - # 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 - ) -} -# @FUNCTION: save_ebuild_env -# @DESCRIPTION: -# echo the current environment to stdout, filtering out redundant info. -save_ebuild_env() { - ( + # 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" - # misc variables set by bash - unset BASH HOSTTYPE IFS MACHTYPE OLDPWD \ - OPTERR OPTIND OSTYPE PS4 PWD SHELL - - # misc variables inherited from the calling environment - unset COLORTERM DISPLAY EDITOR LESS LESSOPEN LOGNAME LS_COLORS PAGER \ - TERM TERMCAP USER - - # There's no need to bloat environment.bz2 with internally defined - # functions and variables, so filter them out if possible. - - unset -f dump_trace diefunc quiet_mode vecho elog_base eqawarn elog \ - esyslog einfo einfon ewarn eerror ebegin _eend eend KV_major \ - KV_minor KV_micro KV_to_int get_KV unset_colors set_colors has \ - hasv hasq qa_source qa_call addread addwrite adddeny addpredict \ - lchown lchgrp esyslog use usev useq has_version portageq \ - best_version use_with use_enable register_die_hook check_KV \ - keepdir unpack strip_duplicate_slashes econf einstall gen_wrapper \ - dyn_setup dyn_unpack dyn_clean into insinto exeinto docinto \ - insopts diropts exeopts libopts abort_handler abort_compile \ - abort_test abort_install dyn_compile dyn_test dyn_install \ - dyn_preinst dyn_help debug-print debug-print-function \ - debug-print-section inherit EXPORT_FUNCTIONS newdepend newrdepend \ - newpdepend do_newdepend remove_path_entry killparent \ - save_ebuild_env filter_readonly_variables preprocess_ebuild_env \ - source_all_bashrcs ebuild_phase ebuild_phase_with_hooks - - # portage config variables and variables set directly by portage - unset ACCEPT_KEYWORDS AUTOCLEAN BAD BRACKET BUILD_PREFIX CLEAN_DELAY \ - COLLISION_IGNORE COLS CONFIG_PROTECT CONFIG_PROTECT_MASK \ - DISTCC_DIR DISTDIR DOC_SYMLINKS_DIR EBUILD_MASTER_PID \ - ECLASSDIR ECLASS_DEPTH EMERGE_DEFAULT_OPTS \ - EMERGE_WARNING_DELAY ENDCOL FAKEROOTKEY FEATURES \ - FETCHCOMMAND FETCHCOMMAND_FTP FETCHCOMMAND_HTTP FETCHCOMMAND_SFTP \ - GENTOO_MIRRORS GOOD HILITE HOME IMAGE \ - KV LAST_E_CMD LAST_E_LEN LD_PRELOAD MOPREFIX \ - NORMAL O PATH PKGDIR PKGUSE PKG_LOGDIR PKG_TMPDIR \ - PORTAGE_ACTUAL_DISTDIR PORTAGE_ARCHLIST PORTAGE_BASHRC \ - PORTAGE_BINHOST_CHUNKSIZE PORTAGE_BINPKG_TMPFILE \ - PORTAGE_BUILDDIR PORTAGE_CALLER \ - PORTAGE_COLORMAP PORTAGE_CONFIGROOT PORTAGE_DEBUG \ - PORTAGE_DEPCACHEDIR PORTAGE_ELOG_CLASSES PORTAGE_ELOG_MAILFROM \ - PORTAGE_ELOG_MAILSUBJECT PORTAGE_ELOG_MAILURI PORTAGE_ELOG_SYSTEM \ - PORTAGE_GID PORTAGE_GPG_DIR PORTAGE_GPG_KEY PORTAGE_INST_GID \ - PORTAGE_INST_UID PORTAGE_LOG_FILE PORTAGE_MASTER_PID \ - PORTAGE_REPO_NAME PORTAGE_RESTRICT \ - PORTAGE_RSYNC_EXTRA_OPTS PORTAGE_RSYNC_OPTS \ - PORTAGE_RSYNC_RETRIES PORTAGE_TMPFS PORTAGE_WORKDIR_MODE PORTDIR \ - PORTDIR_OVERLAY PORT_LOGDIR PROFILE_PATHS PWORKDIR \ - QUICKPKG_DEFAULT_OPTS QA_INTERCEPTORS \ - RC_DEFAULT_INDENT RC_DOT_PATTERN RC_ENDCOL \ - RC_INDENTATION READONLY_EBUILD_METADATA READONLY_PORTAGE_VARS \ - RESUMECOMMAND RESUMECOMMAND_HTTP \ - RESUMECOMMAND_HTTP RESUMECOMMAND_SFTP ROOT ROOTPATH RPMDIR \ - STARTDIR SYNC TMP TMPDIR USE_EXPAND \ - USE_EXPAND_HIDDEN USE_ORDER WARN XARGS - - # Prefix additions - unset DEFAULT_PATH DISTDIR EROOT ED HOME INFOPATH MANPATH - - # Paranoia - unset \ - CVS_RSH ECHANGELOG_USER GENTOO_MIRRORS GROUP HOST HOSTNAME \ - LOGNAME MAIL PORTAGE_GROUP PORTAGE_USER REMOTEHOST \ - SECURITYSESSIONID SSH_AGENT_PID SSH_AUTH_SOCK STY TERMINFO \ - TERM_PROGRAM TERM_PROGRAM_VERSION USER VENDOR \ - __CF_USER_TEXT_ENCODING dir srcdir \ - - set - export - ) + # 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 } # === === === === === === === === === === === === === === === === === === @@ -1609,8 +1544,10 @@ if hasq "depend" "${EBUILD_SH_ARGS}"; then unset BIN_PATH BIN BODY FUNC_SRC fi -if hasq ${EBUILD_PHASE} config info prerm setup \ - && [ ! -f "${T}/environment" ] ; then +# Automatically try to load environment.bz2 whenever +# "${T}/environment" does not exist. +if ! hasq ${EBUILD_SH_ARGS} clean depend && \ + [ ! -f "${T}/environment" ] ; then bzip2 -dc "${EBUILD%/*}"/environment.bz2 > \ "${T}/environment" 2> /dev/null if [ -s "${T}/environment" ] ; then @@ -1678,7 +1615,7 @@ else RDEPEND="${RDEPEND} ${E_RDEPEND}" PDEPEND="${PDEPEND} ${E_PDEPEND}" - unset E_IUSE E_DEPEND E_RDEPEND E_PDEPEND + unset ECLASS E_IUSE E_DEPEND E_RDEPEND E_PDEPEND if [ "${EBUILD_PHASE}" != "depend" ] ; then # Make IUSE defaults backward compatible with all the old shell code. @@ -1812,7 +1749,8 @@ if [ -n "${EBUILD_SH_ARGS}" ] ; then 9>&- fi set +f - #make sure it is writable by our group: + [ -n "${EBUILD_EXIT_STATUS_FILE}" ] && \ + touch "${EBUILD_EXIT_STATUS_FILE}" &>/dev/null exit 0 ;; *) @@ -1823,6 +1761,8 @@ if [ -n "${EBUILD_SH_ARGS}" ] ; then exit 1 ;; esac + [ -n "${EBUILD_EXIT_STATUS_FILE}" ] && \ + touch "${EBUILD_EXIT_STATUS_FILE}" &>/dev/null fi # Save the env only for relevant phases. diff --git a/bin/isolated-functions.sh b/bin/isolated-functions.sh index 3d54a01d7..f08793c93 100755 --- a/bin/isolated-functions.sh +++ b/bin/isolated-functions.sh @@ -98,6 +98,8 @@ diefunc() { eerror "If you need support, post the topmost build error, and the call stack if relevant." [[ -n ${PORTAGE_LOG_FILE} ]] \ && eerror "A complete build log is located at '${PORTAGE_LOG_FILE}'." + [ -f "${T}/environment" ] && \ + eerror "The ebuild environment file is located at '${T}/environment'." if [[ -n ${EBUILD_OVERLAY_ECLASSES} ]] ; then eerror "This ebuild used the following eclasses from overlays:" local x @@ -122,6 +124,9 @@ diefunc() { done fi + [ -n "${EBUILD_EXIT_STATUS_FILE}" ] && \ + touch "${EBUILD_EXIT_STATUS_FILE}" &>/dev/null + # subshell die support kill -s SIGTERM ${EBUILD_MASTER_PID} exit 1 @@ -381,4 +386,66 @@ hasq() { [[ " ${*:2} " == *" $1 "* ]] } +# @FUNCTION: save_ebuild_env +# @DESCRIPTION: +# echo the current environment to stdout, filtering out redundant info. +save_ebuild_env() { + ( + + # misc variables set by bash + unset BASH HOSTNAME HOSTTYPE IFS MACHTYPE OLDPWD \ + OPTERR OPTIND OSTYPE PS4 PWD SHELL SHLVL + + # misc variables inherited from the calling environment + unset COLORTERM DISPLAY EDITOR LESS LESSOPEN LOGNAME LS_COLORS PAGER \ + TERM TERMCAP USER + unset GROUP + + # There's no need to bloat environment.bz2 with internally defined + # functions and variables, so filter them out if possible. + + unset -f dump_trace diefunc quiet_mode vecho elog_base eqawarn elog \ + esyslog einfo einfon ewarn eerror ebegin _eend eend KV_major \ + KV_minor KV_micro KV_to_int get_KV unset_colors set_colors has \ + hasv hasq qa_source qa_call addread addwrite adddeny addpredict \ + lchown lchgrp esyslog use usev useq has_version portageq \ + best_version use_with use_enable register_die_hook check_KV \ + keepdir unpack strip_duplicate_slashes econf einstall gen_wrapper \ + dyn_setup dyn_unpack dyn_clean into insinto exeinto docinto \ + insopts diropts exeopts libopts abort_handler abort_compile \ + abort_test abort_install dyn_compile dyn_test dyn_install \ + dyn_preinst dyn_help debug-print debug-print-function \ + debug-print-section inherit EXPORT_FUNCTIONS newdepend newrdepend \ + newpdepend do_newdepend remove_path_entry killparent \ + save_ebuild_env filter_readonly_variables preprocess_ebuild_env \ + source_all_bashrcs ebuild_phase ebuild_phase_with_hooks + + # portage config variables and variables set directly by portage + unset BAD BRACKET BUILD_PREFIX COLS \ + DISTCC_DIR DISTDIR DOC_SYMLINKS_DIR \ + 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 \ + NORMAL PATH PKGDIR PKGUSE PKG_LOGDIR PKG_TMPDIR \ + PORTAGE_ACTUAL_DISTDIR PORTAGE_ARCHLIST PORTAGE_BASHRC \ + PORTAGE_BINPKG_TMPFILE PORTAGE_BUILDDIR \ + PORTAGE_COLORMAP PORTAGE_CONFIGROOT PORTAGE_DEBUG \ + PORTAGE_DEPCACHEDIR PORTAGE_GID PORTAGE_INST_GID \ + PORTAGE_INST_UID PORTAGE_LOG_FILE PORTAGE_MASTER_PID \ + PORTAGE_REPO_NAME PORTAGE_RESTRICT PORTAGE_WORKDIR_MODE PORTDIR \ + PORTDIR_OVERLAY PROFILE_PATHS PWORKDIR QA_INTERCEPTORS \ + RC_DEFAULT_INDENT RC_DOT_PATTERN RC_ENDCOL \ + RC_INDENTATION READONLY_EBUILD_METADATA READONLY_PORTAGE_VARS \ + ROOT ROOTPATH RPMDIR STARTDIR TMP TMPDIR USE_EXPAND \ + WARN XARGS _RC_GET_KV_CACHE + + # Prefix additions + unset DEFAULT_PATH EROOT PORTAGE_GROUP PORTAGE_USER + + set + export + ) +} + true diff --git a/bin/misc-functions.sh b/bin/misc-functions.sh index f97470c01..7255f8d28 100644 --- a/bin/misc-functions.sh +++ b/bin/misc-functions.sh @@ -679,4 +679,7 @@ if [ -n "${MISC_FUNCTIONS_ARGS}" ]; then done fi +[ -n "${EBUILD_EXIT_STATUS_FILE}" ] && \ + touch "${EBUILD_EXIT_STATUS_FILE}" &>/dev/null + : diff --git a/pym/_emerge/__init__.py b/pym/_emerge/__init__.py index 66f81cca0..ad6febb04 100644 --- a/pym/_emerge/__init__.py +++ b/pym/_emerge/__init__.py @@ -6893,10 +6893,39 @@ def emerge_main(): if "--quiet" not in myopts: portage.deprecated_profile_check() - #Freeze the portdbapi for enhanced performance: - for myroot in trees: - trees[myroot]["porttree"].dbapi.freeze() - del myroot + eclasses_overridden = {} + for mytrees in trees.itervalues(): + mydb = mytrees["porttree"].dbapi + # Freeze the portdbapi for performance (memoize all xmatch results). + mydb.freeze() + eclasses_overridden.update(mydb.eclassdb._master_eclasses_overridden) + del mytrees, mydb + + if eclasses_overridden and \ + settings.get("PORTAGE_ECLASS_WARNING_ENABLE") != "0": + prefix = bad(" * ") + if len(eclasses_overridden) == 1: + writemsg(prefix + "Overlay eclass overrides " + \ + "eclass from PORTDIR:\n", noiselevel=-1) + else: + writemsg(prefix + "Overlay eclasses override " + \ + "eclasses from PORTDIR:\n", noiselevel=-1) + writemsg(prefix + "\n", noiselevel=-1) + for eclass_name in sorted(eclasses_overridden): + writemsg(prefix + " '%s/%s.eclass'\n" % \ + (eclasses_overridden[eclass_name], eclass_name), + noiselevel=-1) + writemsg(prefix + "\n", noiselevel=-1) + msg = "It is best to avoid overridding eclasses from PORTDIR " + \ + "because it will trigger invalidation of cached ebuild metadata " + \ + "that is distributed with the portage tree. If you must " + \ + "override eclasses from PORTDIR then you are advised to run " + \ + "`emerge --regen` after each time that you run `emerge --sync`. " + \ + "Set PORTAGE_ECLASS_WARNING_ENABLE=\"0\" in /etc/make.conf if " + \ + "you would like to disable this warning." + from textwrap import wrap + for line in wrap(msg, 72): + writemsg("%s%s\n" % (prefix, line), noiselevel=-1) if "moo" in myfiles: print """ @@ -6989,6 +7018,13 @@ def emerge_main(): if ("getbinpkg" in settings.features): myopts["--getbinpkg"] = True + if "--buildpkgonly" in myopts: + # --buildpkgonly will not merge anything, so + # it cancels all binary package options. + for opt in ("--getbinpkg", "--getbinpkgonly", + "--usepkg", "--usepkgonly"): + myopts.pop(opt, None) + if "--skipfirst" in myopts and "--resume" not in myopts: myopts["--resume"] = True diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py index eb3877323..8783235af 100644 --- a/pym/portage/__init__.py +++ b/pym/portage/__init__.py @@ -862,7 +862,43 @@ class config(object): Generally if you need data like USE flags, FEATURES, environment variables, virtuals ...etc you look in here. """ - + + # Filter selected variables in the config.environ() method so that + # they don't needlessly propagate down into the ebuild environment. + _environ_filter = [] + + # misc variables inherited from the calling environment + _environ_filter += [ + "CVS_RSH", "ECHANGELOG_USER", + "GPG_AGENT_INFO", "INFOPATH", "MANPATH", + "SSH_AGENT_PID", "SSH_AUTH_SOCK", + "STY", "WINDOW", "XAUTHORITY", + "HOST", "LOGNAME", "MAIL", "REMOTEHOST", + "SECURITYSESSIONID", + "TERMINFO", "TERM_PROGRAM", "TERM_PROGRAM_VERSION", + "VENDOR", "__CF_USER_TEXT_ENCODING", + ] + + # portage config variables and variables set directly by portage + _environ_filter += [ + "ACCEPT_KEYWORDS", "AUTOCLEAN", + "CLEAN_DELAY", "COLLISION_IGNORE", "CONFIG_PROTECT", + "CONFIG_PROTECT_MASK", "EMERGE_DEFAULT_OPTS", + "EMERGE_WARNING_DELAY", "FETCHCOMMAND", "FETCHCOMMAND_FTP", + "FETCHCOMMAND_HTTP", "FETCHCOMMAND_SFTP", + "GENTOO_MIRRORS", "NOCONFMEM", "O", + "PORTAGE_BINHOST_CHUNKSIZE", "PORTAGE_CALLER", + "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_RSYNC_RETRIES", "PORT_LOGDIR", "QUICKPKG_DEFAULT_OPTS", + "RESUMECOMMAND", "RESUMECOMMAND_HTTP", "RESUMECOMMAND_HTTP", + "RESUMECOMMAND_SFTP", "SYNC", "USE_EXPAND_HIDDEN", "USE_ORDER", + ] + + _environ_filter = frozenset(_environ_filter) + def __init__(self, clone=None, mycpv=None, config_profile_path=None, config_incrementals=None, config_root=None, target_root=None, local_config=True): @@ -2451,6 +2487,13 @@ class config(object): return d[k] return x + def pop(self, k, x=None): + self.modifying() + v = x + for d in reversed(self.lookuplist): + v = d.pop(k, v) + return v + def has_key(self,mykey): return mykey in self @@ -2492,7 +2535,10 @@ class config(object): def environ(self): "return our locally-maintained environment" mydict={} + environ_filter = self._environ_filter for x in self: + if x in environ_filter: + continue myvalue = self[x] if not isinstance(myvalue, basestring): writemsg("!!! Non-string value in config: %s=%s\n" % \ @@ -3538,8 +3584,18 @@ def spawnebuild(mydo,actionmap,mysettings,debug,alwaysdep=0,logfile=None): return retval kwargs = actionmap[mydo]["args"] 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"] = "" + msg = _doebuild_exit_status_check( + mydo, mysettings.get("EBUILD_EXIT_STATUS_FILE")) + if msg: + phase_retval = 1 + from textwrap import wrap + from portage.elog.messages import eerror + for l in wrap(msg, 72): + eerror(l, phase=mydo, key=mysettings.mycpv) if "userpriv" in mysettings.features and \ not kwargs["droppriv"] and secpass >= 2: @@ -3751,6 +3807,8 @@ def doebuild_environment(myebuild, mydo, myroot, mysettings, debug, use_cache, m mysettings["PORTAGE_BASHRC"] = os.path.join( mysettings["PORTAGE_CONFIGROOT"], EBUILD_SH_ENV_FILE.lstrip(os.path.sep)) + mysettings["EBUILD_EXIT_STATUS_FILE"] = os.path.join( + mysettings["PORTAGE_BUILDDIR"], ".exit_status") #set up KV variable -- DEP SPEEDUP :: Don't waste time. Keep var persistent. if (mydo!="depend") or not mysettings.has_key("KV"): @@ -3954,6 +4012,37 @@ 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): + """ + Returns an error string if the shell appeared + to exit unsuccessfully, None otherwise. + """ + if not exit_status_file or \ + os.path.exists(exit_status_file): + return None + msg = ("The ebuild phase '%s' has exited " % mydo) + \ + "unexpectedly. This type of behavior " + \ + "is known to be triggered " + \ + "by things such as failed variable " + \ + "assignments (bug #190128) or bad substitution " + \ + "errors (bug #200313)." + return msg + +def _doebuild_exit_status_unlink(exit_status_file): + """ + Double check to make sure it really doesn't exist + and raise an OSError if it still does (it shouldn't). + OSError if necessary. + """ + if not exit_status_file: + return + try: + os.unlink(exit_status_file) + except OSError: + pass + if os.path.exists(exit_status_file): + os.unlink(exit_status_file) + _doebuild_manifest_exempt_depend = 0 _doebuild_manifest_checked = None @@ -4096,6 +4185,19 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, return 1 _doebuild_manifest_checked = manifest_path + def exit_status_check(retval): + if retval != os.EX_OK: + return retval + msg = _doebuild_exit_status_check( + mydo, mysettings.get("EBUILD_EXIT_STATUS_FILE")) + if msg: + retval = 1 + from textwrap import wrap + from portage.elog.messages import eerror + for l in wrap(msg, 72): + eerror(l, phase=mydo, key=mysettings.mycpv) + return retval + logfile=None builddir_lock = None tmpdir = None @@ -4205,9 +4307,14 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, "correct your PORTAGE_TMPDIR setting.\n", noiselevel=-1) return 1 + if mydo == "unmerge": + return unmerge(mysettings["CATEGORY"], + mysettings["PF"], myroot, mysettings, vartree=vartree) + # Build directory creation isn't required for any of these. have_build_dirs = False - if mydo not in ("digest", "fetch", "help", "manifest"): + if mydo not in ("clean", "cleanrm", "digest", + "fetch", "help", "manifest"): mystatus = prepare_build_dirs(myroot, mysettings, cleanup) if mystatus: return mystatus @@ -4216,9 +4323,11 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, logfile = mysettings.get("PORTAGE_LOG_FILE") if logfile and not os.access(os.path.dirname(logfile), os.W_OK): logfile = None - if mydo == "unmerge": - return unmerge(mysettings["CATEGORY"], - mysettings["PF"], myroot, mysettings, vartree=vartree) + if have_build_dirs: + _doebuild_exit_status_unlink( + mysettings.get("EBUILD_EXIT_STATUS_FILE")) + else: + mysettings.pop("EBUILD_EXIT_STATUS_FILE", None) # if any of these are being called, handle them -- running them out of # the sandbox -- and stop now. @@ -4237,6 +4346,7 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, mysettings.load_infodir(infodir) retval = spawn(EBUILD_SH_BINARY + " " + mydo, mysettings, debug=debug, free=1, logfile=logfile) + retval = exit_status_check(retval) if secpass >= 2: """ Privileged phases may have left files that need to be made writable to a less privileged user.""" @@ -4247,6 +4357,7 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, elif mydo == "preinst": 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. @@ -4264,6 +4375,7 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, mysettings.load_infodir(mysettings["O"]) 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. @@ -4277,8 +4389,10 @@ 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"]) - return 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 mycpv = "/".join((mysettings["CATEGORY"], mysettings["PF"])) @@ -4456,7 +4570,9 @@ 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: + if retval == os.EX_OK: + retval = exit_status_check(retval) + else: # 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/eclass_cache.py b/pym/portage/eclass_cache.py index 806505757..de20d307c 100644 --- a/pym/portage/eclass_cache.py +++ b/pym/portage/eclass_cache.py @@ -23,6 +23,7 @@ class cache(object): self.porttrees = [self.porttree_root]+overlays self.porttrees = tuple(map(normalize_path, self.porttrees)) self._master_eclass_root = os.path.join(self.porttrees[0],"eclass") + self._master_eclasses_overridden = {} self.update_eclasses() def close_caches(self): @@ -41,6 +42,7 @@ class cache(object): def update_eclasses(self): self.eclasses = {} self._eclass_locations = {} + master_eclasses = {} eclass_len = len(".eclass") ignored_listdir_errnos = (errno.ENOENT, errno.ENOTDIR) for x in [normalize_path(os.path.join(y,"eclass")) for y in self.porttrees]: @@ -63,7 +65,13 @@ class cache(object): ys=y[:-eclass_len] self.eclasses[ys] = (x, long(mtime)) self._eclass_locations[ys] = x - + if x == self._master_eclass_root: + master_eclasses[ys] = mtime + else: + master_mtime = master_eclasses.get(ys) + if master_mtime and master_mtime != mtime: + self._master_eclasses_overridden[ys] = x + def is_eclass_data_valid(self, ec_dict): if not isinstance(ec_dict, dict): return False -- 2.26.2