Improve handling of exit routines to ensure we respect the exit status.

As part of this, change atexit to receive the name of a function instead of
just a chunk of code.  This way, we can also control the return value of
each individual atexit function and report it to the user, should these
routines fail when the program apparently succeeded.

--
MOS_MIGRATED_REVID=115575895
diff --git a/scripts/bootstrap/buildenv.sh b/scripts/bootstrap/buildenv.sh
index 63d0897..fa69204 100755
--- a/scripts/bootstrap/buildenv.sh
+++ b/scripts/bootstrap/buildenv.sh
@@ -43,25 +43,71 @@
   EXE_EXT=".exe"
 esac
 
-ATEXIT_=""
+# List of functions to invoke on exit.
+ATEXIT_HANDLERS=
+
+# Registers a function to be invoked on exit.
+#
+# The handlers will be invoked at exit time in the order they were registered.
+# See comments in run_atexit for more details.
 function atexit() {
-  ATEXIT_="$1; ${ATEXIT_}"
-  trap "{ ${ATEXIT_} }" EXIT
+  local handler="${1}"; shift
+
+  [ -n "${ATEXIT_HANDLERS}" ] || trap 'run_atexit_handlers $?' EXIT
+  ATEXIT_HANDLERS="${ATEXIT_HANDLERS} ${handler}"
+}
+
+# Exit routine to run all registered atexit handlers.
+#
+# If the program exited with an error, this exit routine will also exit with the
+# same error.  However, if the program exited successfully, this exit routine
+# will only exit successfully if the atexit handlers succeed.
+function run_atexit_handlers() {
+  local exit_code="$?"
+
+  local failed=no
+  for handler in ${ATEXIT_HANDLERS}; do
+    eval "${handler}" || failed=yes
+  done
+
+  trap - EXIT  # Reset exit handler to prevent double execution.
+  if [ ${exit_code} -ne 0 ]; then
+    exit ${exit_code}
+  else
+    if [ "${failed}" = yes ]; then
+      echo "Program tried to exit successfully but atexit routines failed" 1>&2
+      exit 1
+    else
+      exit 0
+    fi
+  fi
 }
 
 function tempdir() {
   local tmp=${TMPDIR:-/tmp}
   local DIR="$(mktemp -d ${tmp%%/}/bazel.XXXXXXXX)"
   mkdir -p "${DIR}"
-  atexit "rm -fr ${DIR}"
+  eval "cleanup_tempdir() { rm -rf '${DIR}'; }"
+  atexit cleanup_tempdir
   NEW_TMPDIR="${DIR}"
 }
 tempdir
 OUTPUT_DIR=${NEW_TMPDIR}
 errfile=${OUTPUT_DIR}/errors
-atexit "if [ -f ${errfile} ]; then cat ${errfile} >&2; fi"
+eval "cleanup_errfile() {
+        if [ -f '${errfile}' ]; then
+          cat '${errfile}' 1>&2;
+        fi;
+      }"
+atexit cleanup_errfile
 phasefile=${OUTPUT_DIR}/phase
-atexit "if [ -f ${phasefile} ]; then echo >&2; cat ${phasefile} >&2; fi"
+eval "cleanup_phasefile() {
+        if [ -f '${phasefile}' ]; then
+          echo 1>&2;
+          cat '${phasefile}' 1>&2;
+        fi;
+      }"
+atexit cleanup_phasefile
 
 function run_silent() {
   echo "${@}" >${errfile}