From dd98145bc182af39645ba6bea0e5441afd84a1d3 Mon Sep 17 00:00:00 2001 From: "Yann E. MORIN\"" Date: Sat, 6 Oct 2012 23:48:07 +0200 Subject: scripts: add option to start an interactive debug shell Add an option that, when a command fails: - starts an interactive shell with the failed command's environment - attempts re-execution of the failed command, continues, or aborts at user's whim. Before starting the debug-shell, the backtrace is printed. When exiting for an abort, the standard error message is printed. Based on an idea and a patch from: Johannes Stezenbach http://sourceware.org/ml/crossgcc/2012-09/msg00144.html Signed-off-by: Johannes Stezenbach [yann.morin.1998@free.fr: integrate in the fault handler] Signed-off-by: "Yann E. MORIN" Acked-by: Johannes Stezenbach Patchwork-Id: 191571 Patchwork-Id: 191668 diff --git a/config/global/ct-behave.in b/config/global/ct-behave.in index bbe4324..6256a34 100644 --- a/config/global/ct-behave.in +++ b/config/global/ct-behave.in @@ -87,4 +87,23 @@ config NO_OVERIDE_LC_MESSAGES Say N, please. +config DEBUG_INTERACTIVE + bool + prompt "Interactive shell on failed commands" + help + If you say 'y' here, then an interactive shell will be spawned for + each failed command. + + This shell will have the same environment that the failed command + was run with, and the working directory will be set to the directory + the failed command was run in. + + After you fix the issue, you can exit the interactive shell with any + of these exit codes: + 1 the issue was fixed, continue the build with the next command + 2 the issue was fixed, re-run the failed command + 3 abort the build + + Note: '2' is only possible for commands run via CT_DoExecLog, though. + endif diff --git a/scripts/crosstool-NG.sh.in b/scripts/crosstool-NG.sh.in index 1f1ffdb..a7a8015 100644 --- a/scripts/crosstool-NG.sh.in +++ b/scripts/crosstool-NG.sh.in @@ -25,6 +25,14 @@ . .config.2 # Yes! We can do full logging from now on! +# If we want an interactive debug-shell, we must ensure these FDs +# are indeed connected to a terminal (and not redirected in any way). +if [ "${CT_DEBUG_INTERACTIVE}" = "y" -a ! \( -t 0 -a -t 6 -a -t 2 \) ]; then + CT_DoLog ERROR "Can't spawn interactive debug-shell," + CT_DoLog ERROR "because stdout/stderr has been redirected." + exit 1 +fi + # Override the locale early, in case we ever translate crosstool-NG messages if [ -z "${CT_NO_OVERIDE_LC_MESSAGES}" ]; then export LC_ALL=C diff --git a/scripts/functions b/scripts/functions index 5180726..7bb58ab 100644 --- a/scripts/functions +++ b/scripts/functions @@ -5,6 +5,8 @@ # Prepare the fault handler CT_OnError() { local ret=$? + local result + local old_trap local intro local file line func local step step_depth @@ -35,6 +37,62 @@ CT_OnError() { CT_DoLog ERROR ">> ${intro}: ${func}[${file}${line}]" intro=" called from" done + + # If the user asked for interactive debugging, dump him/her to a shell + if [ "${CT_DEBUG_INTERACTIVE}" = "y" ]; then + # We do not want this sub-shell exit status to be caught, because + # it is absolutely legit that it exits with non-zero. + # Save the trap handler to restore it after our debug-shell + old_trap="$(trap -p ERR)" + trap -- ERR + ( + exec >&6 + printf "\r \n\nCurrent command" + if [ -n "${cur_cmd}" ]; then + printf ":\n %s\n" "${cur_cmd}" + else + printf " (unknown), " + fi + printf "exited with error code: %d\n" ${ret} + printf "Please fix it up and finish by exiting the shell with one of these values:\n" + printf " 1 fixed, continue with next build command\n" + if [ -n "${cur_cmd}" ]; then + printf " 2 repeat this build command\n" + fi + printf " 3 abort build\n\n" + while true; do + ${bash} --rcfile <(printf "PS1='ct-ng:\w> '\nPROMPT_COMMAND=''\n") -i + result=$? + case $result in + 1) printf "\nContinuing past the failed command.\n\n" + break + ;; + 2) if [ -n "${cur_cmd}" ]; then + printf "\nRe-trying last command.\n\n" + break + fi + ;;& + 3) break;; + *) printf "\nPlease exit with one of these values:\n" + printf " 1 fixed, continue with next build command\n" + if [ -n "${cur_cmd}" ]; then + printf " 2 repeat this build command\n" + fi + printf " 3 abort build\n" + ;; + esac + done + exit $result + ) + result=$? + # Restore the trap handler + eval "${old_trap}" + case "${result}" in + 1) rm -f "${CT_WORK_DIR}/backtrace"; return;; + 2) rm -f "${CT_WORK_DIR}/backtrace"; touch "${CT_BUILD_DIR}/repeat"; return;; + # 3 is an abort, continue... + esac + fi fi # And finally, in top-level shell, print some hints @@ -157,10 +215,11 @@ CT_DoLog() { # Usage: CT_DoExecLog [VAR=val...] [parameters...] CT_DoExecLog() { local level="$1" + local cur_cmd shift ( for i in "$@"; do - tmp_log+="'${i}' " + cur_cmd+="'${i}' " done while true; do case "${1}" in @@ -168,8 +227,39 @@ CT_DoExecLog() { *) break;; esac done - CT_DoLog DEBUG "==> Executing: ${tmp_log}" - "${@}" 2>&1 |CT_DoLog "${level}" + # This while-loop goes hand-in-hand with the ERR trap handler: + # - if the command terminates successfully, then we hit the break + # statement, and we exit the loop + # - if the command terminates in error, then the ERR handler kicks + # in, then: + # - if the user did *not* ask for interactive debugging, the ERR + # handler exits, and we hit the end of the sub-shell + # - if the user did ask for interactive debugging, the ERR handler + # spawns a shell. Upon termination of this shell, the ERR handler + # examines the exit status of the shell: + # - if 1, the ERR handler returns; then we hit the else statement, + # then the break, and we exit the 'while' loop, to continue the + # build; + # - if 2, the ERR handler touches the repeat file, and returns; + # then we hit the if statement, and we loop for one more + # iteration; + # - if 3, the ERR handler exits with the command's exit status, + # and we're dead; + # - for any other exit status of the shell, the ERR handler + # prints an informational message, and respawns the shell + # + # This allows a user to get an interactive shell that has the same + # environment (PATH and so on) that the failed command was ran with. + while true; do + rm -f "${CT_BUILD_DIR}/repeat" + CT_DoLog DEBUG "==> Executing: ${cur_cmd}" + "${@}" 2>&1 |CT_DoLog "${level}" + if [ -f "${CT_BUILD_DIR}/repeat" ]; then + continue + else + break + fi + done ) # Catch failure of the sub-shell [ $? -eq 0 ] -- cgit v0.10.2-6-g49f6