[bitbake-devel,2/3] build: print the originating filename/lineno of failing Bash shell functions

Submitted by Chris Laplante via lists.openembedded.org on Aug. 7, 2020, 2:23 p.m. | Patch ID: 175131

Details

Message ID 20200807142343.8648-3-chris.laplante@agilent.com
State New
Headers show

Commit Message

This patch modifies the shell trap code which is injected into shell tasks.
The behavior for 'sh' shells is unchanged. But now when a Bash shell function
fails, the following happens:
1. At the line on which an error occurred, the enclosing function name is grabbed.
1. If the shell is 'sh', simply report the return code (just as it did before)

Before:
| install: cannot stat 'source': No such file or directory
| WARNING: /home/laplante/repos/oe-core/build/tmp-glibc/work/core2-64-oe-linux/libsolv/0.7.14-r0/temp/run.do_compile.95318:1 exit 1 from 'install -m 0644 source dest'

After:
| install: cannot stat 'source': No such file or directory
| WARNING: /home/laplante/repos/oe-core/build/tmp-glibc/work/core2-64-oe-linux/libsolv/0.7.14-r0/temp/run.do_compile.95318:139 exit 1 from 'install -m 0644 source dest'
| INFO: /home/laplante/repos/oe-core/meta/recipes-extended/libsolv/libsolv_0.7.14.bb:32 is where original shell function 'my_incorrect_function' is declared

Signed-off-by: Chris Laplante <chris.laplante@agilent.com>
---
 lib/bb/build.py | 49 ++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 38 insertions(+), 11 deletions(-)

Patch hide | download patch | download mbox

diff --git a/lib/bb/build.py b/lib/bb/build.py
index 977b02fc..bbea20c6 100644
--- a/lib/bb/build.py
+++ b/lib/bb/build.py
@@ -304,19 +304,46 @@  def exec_func_python(func, d, runfile, cwd=None):
 def shell_trap_code():
     return '''#!/bin/sh\n
 # Emit a useful diagnostic if something fails:
-bb_exit_handler() {
+bb_sh_exit_handler() {
     ret=$?
-    case $ret in
-    0)  ;;
-    *)  case $BASH_VERSION in
-        "") echo "WARNING: exit code $ret from a shell command.";;
-        *)  echo "WARNING: ${BASH_SOURCE[0]}:${BASH_LINENO[0]} exit $ret from '$BASH_COMMAND'";;
-        esac
-        exit $ret
-    esac
+    if [ "$ret" != 0 ]; then
+        echo "WARNING: exit code $ret from a shell command."
+    fi
+    exit $ret
 }
-trap 'bb_exit_handler' 0
-set -e
+
+bb_bash_err_handler() {
+    ret=$?
+    echo "WARNING: ${BASH_SOURCE[0]}:${BASH_LINENO[0]} exit $ret from '$BASH_COMMAND'"
+
+    # Determine the line on which the failing shell function is declared
+    shopt -s extdebug
+    function_line=$(declare -F "${FUNCNAME[1]}" | awk '{print $2}')
+    shopt -u extdebug
+
+    # Grab the special comment that precedes the function declaration - this comment contains the filename and lineno
+    # that BitBake tracked as being the original location for the emitted shell function.
+    prev_line=$((function_line - 1))
+    function_location=$(sed "${prev_line}q;d" "${BASH_SOURCE[0]}")
+    pattern='# line: ([[:digit:]]+), file: (.+)'
+    if [[ $function_location =~ $pattern ]]; then
+        relative_line=$((BASH_LINENO[0] - function_line))
+        # TODO: handle 'autogenerated' (e.g. from _append) by walking up the call chain to the nearest real file?
+        # TODO: actually just fix BitBake to include filename/lineno information when _append is encountered, maybe as
+        #       separate varflags.
+        echo "INFO: ${BASH_REMATCH[2]}:$((BASH_REMATCH[1] + relative_line)) is where original shell function '${FUNCNAME[1]}' is declared"
+    fi
+
+    exit $ret
+}
+
+case $BASH_VERSION in
+"") trap 'bb_sh_exit_handler' 0
+    set -e
+    ;;
+*)  trap 'bb_bash_err_handler' ERR
+    set -eE
+esac
 '''
 
 def create_progress_handler(func, progress, logfile, d):