summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZac Medico <zmedico@gentoo.org>2007-11-26 11:58:31 +0000
committerZac Medico <zmedico@gentoo.org>2007-11-26 11:58:31 +0000
commit639741c0eb011ba39231e28bd6e76e5fc8ccdc0c (patch)
tree4825df5e37bf1045e6fe87574de792b182bbae80
parente71261fb424af2f70073384968d84dc1b623eb5a (diff)
downloadportage-639741c0eb011ba39231e28bd6e76e5fc8ccdc0c.tar.gz
portage-639741c0eb011ba39231e28bd6e76e5fc8ccdc0c.tar.bz2
portage-639741c0eb011ba39231e28bd6e76e5fc8ccdc0c.zip
Bug #200313 - Detect and report when an ebuild phase
exits 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. (trunk r8682) svn path=/main/branches/2.1.2/; revision=8684
-rwxr-xr-xbin/ebuild.sh3
-rw-r--r--bin/isolated-functions.sh5
-rwxr-xr-xbin/misc-functions.sh2
-rw-r--r--pym/portage.py83
4 files changed, 89 insertions, 4 deletions
diff --git a/bin/ebuild.sh b/bin/ebuild.sh
index 46651e76a..a6969c9d8 100755
--- a/bin/ebuild.sh
+++ b/bin/ebuild.sh
@@ -1726,7 +1726,7 @@ if [ -n "${EBUILD_SH_ARGS}" ] ; then
9>&-
fi
set +f
- #make sure it is writable by our group:
+ touch "${EBUILD_EXIT_STATUS_FILE}" &>/dev/null
exit 0
;;
*)
@@ -1737,6 +1737,7 @@ if [ -n "${EBUILD_SH_ARGS}" ] ; then
exit 1
;;
esac
+ 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 ae418389d..9eaf9bffb 100644
--- a/bin/isolated-functions.sh
+++ b/bin/isolated-functions.sh
@@ -124,6 +124,8 @@ diefunc() {
done
fi
+ touch "${EBUILD_EXIT_STATUS_FILE}" &>/dev/null
+
# subshell die support
kill -s SIGTERM ${EBUILD_MASTER_PID}
exit 1
@@ -416,7 +418,8 @@ save_ebuild_env() {
# portage config variables and variables set directly by portage
unset BAD BRACKET BUILD_PREFIX COLS \
- DISTCC_DIR DISTDIR DOC_SYMLINKS_DIR EBUILD_MASTER_PID \
+ 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 \
diff --git a/bin/misc-functions.sh b/bin/misc-functions.sh
index 31ad9d8e8..633b3c37d 100755
--- a/bin/misc-functions.sh
+++ b/bin/misc-functions.sh
@@ -587,4 +587,6 @@ if [ -n "${MISC_FUNCTIONS_ARGS}" ]; then
done
fi
+touch "${EBUILD_EXIT_STATUS_FILE}" &>/dev/null
+
:
diff --git a/pym/portage.py b/pym/portage.py
index 31902c7e3..e62355a9a 100644
--- a/pym/portage.py
+++ b/pym/portage.py
@@ -3510,8 +3510,23 @@ 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
+ cmd = "source '%s/isolated-functions.sh' ; " % \
+ PORTAGE_BIN_PATH
+ for l in wrap(msg, 72):
+ cmd += "eerror \"%s\" ; " % l
+ mysettings["EBUILD_PHASE"] = mydo
+ portage_exec.spawn(["bash", "-c", cmd],
+ env=mysettings.environ())
+ mysettings["EBUILD_PHASE"] = ""
if "userpriv" in mysettings.features and \
not kwargs["droppriv"] and secpass >= 2:
@@ -3693,6 +3708,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"):
@@ -3896,6 +3913,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 is 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
@@ -4038,6 +4086,24 @@ 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
+ cmd = "source '%s/isolated-functions.sh' ; " % \
+ PORTAGE_BIN_PATH
+ for l in wrap(msg, 72):
+ cmd += "eerror \"%s\" ; " % l
+ mysettings["EBUILD_PHASE"] = mydo
+ portage_exec.spawn(["bash", "-c", cmd],
+ env=mysettings.environ())
+ mysettings["EBUILD_PHASE"] = ""
+ return retval
+
logfile=None
builddir_lock = None
tmpdir = None
@@ -4158,6 +4224,12 @@ 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 have_build_dirs:
+ _doebuild_exit_status_unlink(
+ mysettings.get("EBUILD_EXIT_STATUS_FILE"))
+ else:
+ mysettings.pop("EBUILD_EXIT_STATUS_FILE", None)
if mydo == "unmerge":
return unmerge(mysettings["CATEGORY"],
mysettings["PF"], myroot, mysettings, vartree=vartree)
@@ -4179,6 +4251,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."""
@@ -4189,6 +4262,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.
@@ -4206,6 +4280,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.
@@ -4219,8 +4294,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"]))
@@ -4402,7 +4479,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.