From patchwork Mon Oct 15 19:53:56 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [1,of,1] scripts: add option to start an interactive debug shell Date: Mon, 15 Oct 2012 09:53:56 -0000 From: Yann E. MORIN X-Patchwork-Id: 191657 Message-Id: <82c19a72f25a7b399f6b.1350330836@treguer.bzh.lan> To: Johannes Stezenbach Cc: crossgcc@sourceware.org # HG changeset patch # User "Yann E. MORIN" # Date 1349560087 -7200 # Node ID 82c19a72f25a7b399f6be4e2c8c1cecc45d8c171 # Parent 2a616dab6531ad82876c3252cd2e1cb873375c3f 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, continue, or aborts at user's whim. Based on an idea and a patch from: Johannes Stezenbach http://sourceware.org/ml/crossgcc/2012-09/msg00144.html Signed-off-by: "Yann E. MORIN" Cc: Johannes Stezenbach --- For unsubscribe information see http://sourceware.org/lists.html#faq diff --git a/config/global/ct-behave.in b/config/global/ct-behave.in --- a/config/global/ct-behave.in +++ b/config/global/ct-behave.in @@ -87,4 +87,22 @@ Say N, please. +config DEBUG_INTERACTIVE + bool + prompt "Interactive shell on failed commands" + depends on EXPERIMENTAL + help + If you say 'y' here, then an interactive shell will be spawned for + each failed command run via CT_DoExecLog. + + 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 + endif diff --git a/scripts/functions b/scripts/functions --- a/scripts/functions +++ b/scripts/functions @@ -5,10 +5,45 @@ # Prepare the fault handler CT_OnError() { local ret=$? + local old_trap result local intro local file line func local step step_depth + # 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 + old_trap="$(trap -p ERR)" + trap ERR + ( + exec >&6 + printf "Current command\n %s\n" "${cur_cmd}" + printf "exited with error code: %d\n" ${ret} + printf "Please fix it up and finish by exiting the shell.\n" + while true; do + bash --rcfile <(echo "PS1='ct-ng:\w> '") -i + result=$? + case $result in + 1) break;; + 2) break;; + 3) break;; + *) echo "please exit with one of these values:" + echo "1 fixed, continue with next build command" + echo "2 repeat this build command" + echo "3 abort build" + ;; + esac + done + exit $result + ) + result=$? + case "${result}" in + 1) return;; + 2) touch "${CT_BUILD_DIR}/repeat"; return;; + # 3 is an abort, continue... + esac + fi + # To avoid printing the backtace for each sub-shell # up to the top-level, just remember we've dumped it if [ ! -f "${CT_BUILD_DIR}/backtrace" ]; then @@ -157,10 +192,11 @@ # 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 +204,39 @@ *) 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 ]