summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZac Medico <zmedico@gentoo.org>2007-11-26 11:23:17 +0000
committerZac Medico <zmedico@gentoo.org>2007-11-26 11:23:17 +0000
commit83b4b8792f8d600ce7630fa6d917c02ac93c856e (patch)
tree76500afc56f952dba4e2b3c1e1a155c6e88ad93d
parent9116d76089d00491f5f20a4d834408b7ff45c983 (diff)
downloadportage-83b4b8792f8d600ce7630fa6d917c02ac93c856e.tar.gz
portage-83b4b8792f8d600ce7630fa6d917c02ac93c856e.tar.bz2
portage-83b4b8792f8d600ce7630fa6d917c02ac93c856e.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. svn path=/main/trunk/; revision=8682
-rwxr-xr-xbin/ebuild.sh3
-rwxr-xr-xbin/isolated-functions.sh5
-rwxr-xr-xbin/misc-functions.sh2
-rw-r--r--pym/portage/__init__.py73
4 files changed, 79 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 100755
--- 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 2e860453e..450e71631 100755
--- a/bin/misc-functions.sh
+++ b/bin/misc-functions.sh
@@ -593,4 +593,6 @@ if [ -n "${MISC_FUNCTIONS_ARGS}" ]; then
done
fi
+touch "${EBUILD_EXIT_STATUS_FILE}" &>/dev/null
+
:
diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py
index 28d5eeed5..04b73b1b8 100644
--- a/pym/portage/__init__.py
+++ b/pym/portage/__init__.py
@@ -3560,8 +3560,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:
@@ -3744,6 +3754,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"):
@@ -3947,6 +3959,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
@@ -4089,6 +4132,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
@@ -4209,6 +4265,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)
@@ -4230,6 +4292,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."""
@@ -4240,6 +4303,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.
@@ -4257,6 +4321,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.
@@ -4270,8 +4335,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"]))
@@ -4449,7 +4516,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.