| #!/bin/sh -efu |
| |
| # Copyright 2011-2012 Intel Corporation |
| # Author: Artem Bityutskiy |
| # License: GPLv2 |
| |
| . shell-error |
| |
| if [ -z "${__included_aiaiai_sh_functions-}" ]; then |
| __included_aiaiai_sh_functions=1 |
| |
| # Print an error message and exit |
| # Usage: die <message> |
| die() |
| { |
| fatal "Fatal error: $1" |
| } |
| |
| # Print a separator line to stdout |
| print_separator() |
| { |
| local i=0 |
| |
| while [ $i -lt 80 ]; do |
| i="$(($i+1))"; |
| printf "-"; |
| done |
| echo |
| } |
| |
| # Check if dash is available and we are not running in dash |
| can_switch_to_dash() |
| { |
| if command -v "dash" >/dev/null 2>&1; then |
| if [ -n "${BASH_VERSION:-}" ]; then |
| return 0 |
| fi |
| fi |
| |
| return 1 |
| } |
| |
| # Die if a program is not in PATH |
| # Usage: program_required <program_name> |
| program_required() |
| { |
| local prg="$1"; shift |
| local msg="$1"; shift |
| |
| if ! command -v "$prg" >/dev/null 2>&1; then |
| message "Program \"$prg\" is required but not found in PATH" |
| if [ -n "$msg" ]; then |
| die "$msg" |
| else |
| exit 1 |
| fi |
| fi |
| } |
| |
| # Some tools in the "helpers" subdirectory have to be compiled before they can |
| # be sued. When Aiaiai is used from the source tree (as opposed to being |
| # installed from an RPM package), the user may froget to compile the tools. |
| # This function tries to compile them. |
| # |
| # Usage: compile_helpers <srcdir> |
| # |
| # where <srcdir> is the directory where the helper tools are supposed to live. |
| compile_helpers() |
| { |
| local srcdir="$1" |
| local tools="remap-log aiaiai-locker" |
| |
| for tool in $tools; do |
| if command -v "$tool" >/dev/null 2>&1; then |
| continue |
| fi |
| |
| if [ -f "$srcdir/${tool}.c" ]; then |
| make -C "$srcdir" "$tool" >/dev/null 2>&1 ||: |
| fi |
| done |
| } |
| |
| # Fetch the first occurrence of header "$1" from the mbox file |
| # Usage: fetch_header <header_name> < <mbox_file> |
| fetch_header() |
| { |
| local hdr="$1" |
| |
| program_required "formail" "" |
| |
| # Take only the first occurrence of the header |
| formail -z -c -x "$hdr:" | head -n1 | aiaiai-decode-rfc-2047 |
| } |
| |
| # Fetch all occurrences of header "$1" from the first e-mail in the mbox file |
| # Usage: fetch_header <header_name> < <mbox_file> |
| fetch_all_headers() |
| { |
| local hdr="$1" |
| |
| program_required "formail" "" |
| |
| # Take every occurrence of the header. This will only take occurrences |
| # from the first of a combined mbox. |
| formail -z -c -x "$hdr:" | aiaiai-decode-rfc-2047 |
| } |
| |
| # Similar to fetch_header, but exits with code 1 if the header hasn't been |
| # found, and has a little bit different interface (the result is stored in |
| # "<var>"). |
| # |
| # Usage: fetch_header_or_die <var> <header_name> < <mbox_file> |
| fetch_header_or_die() |
| { |
| local var="$1"; shift |
| local hdr="$1"; shift |
| local res="$(fetch_header "$hdr")" |
| |
| [ -n "$res" ] || die "Cannot find the \"$hdr:\" header" |
| |
| eval "$var=\"\$res\"" |
| } |
| |
| fetch_header_per_patch() |
| { |
| local hdr="$1" |
| |
| program_required "formail" "" |
| |
| # Take only the first occurrence of the header per message |
| formail -s sh -c "formail -z -c -x \"$hdr:\" | head -n1" | aiaiai-decode-rfc-2047 |
| } |
| |
| # Insert a header into the given mbox file |
| # Usage: insert_header <mbox_file> <header> |
| insert_header() |
| { |
| local mbox="$1"; shift |
| local header="$1"; shift |
| |
| # The below trick allows us to avoid creating a separate temporary |
| # file; open the "$mbox" file, unlink, use the open file descriptor for |
| # reading and redirect the output to the new version of the "$mbox" |
| # file. We could instead use the "sponge" tool, however. |
| exec 3<$mbox |
| rm $verbose "$mbox" >&2 |
| verbose "Adding \"$header\"" |
| formail -s formail -I "$header" <&3 > "$mbox" |
| exec 3<&- |
| } |
| |
| git_dir() |
| { |
| local path="$1" |
| local dotgit="$path/.git" |
| |
| if [ -d "$dotgit" ]; then |
| printf "%s" "$dotgit" |
| elif [ -d "$path" ]; then |
| printf "%s" "$path" |
| else |
| die "not a git repository: $path" |
| fi |
| } |
| |
| # Apply a patch. In case of error, print user-friendly diagnostic messages to |
| # stdin. |
| # Usage: apply_patch < <mbox_file> |
| apply_patch() |
| { |
| local am cmt |
| |
| program_required "patch" "" |
| |
| cmt="$(git rev-parse "HEAD^{commit}")" |
| |
| am="$(formail -s aiaiai-extract-patches | git am --3way 2>&1)" || { |
| cat <<EOF |
| Failed to apply patch(es) with git am on top of: |
| $(git log -1 --oneline "$cmt") |
| |
| $am |
| |
| Results of "patch --merge=diff3 -p1 < .git/rebase-apply/patch": |
| |
| $(patch --merge=diff3 -p1 < .git/rebase-apply/patch 2>&1) |
| |
| $(print_separator) |
| |
| $(git diff --no-color | sed 's/^/> /') |
| EOF |
| return 1 |
| } |
| } |
| |
| # A helper function for 'build_failure()'. This function expects the properly |
| # formatted build log a stdin and outputs user-readable failure report to |
| # stdout. |
| __print_build_log() |
| { |
| local config="$(leave_first "$1")"; |
| local arch="$(leave_second "$1")"; shift |
| local commit_id="$1"; shift |
| local commit_info="$(git log -1 --oneline "$commit_id")" |
| local commit_nickname="${1:-""}" |
| |
| cat <<EOF |
| Failed to build $commit_nickname: $commit_info |
| Configuration: "$config${arch:+", architecture $arch"}". |
| |
| $(cat) |
| EOF |
| } |
| |
| # Format a build failure report. |
| # Usage: build_failure <defconfig> <commit_id> <commit_nickname> < <build_log> |
| build_failure() |
| { |
| # The build log might have been generated with multiple jobs which |
| # means it is probably messy and the error message is probably not at |
| # the very end. To make it more probable that we actually print the |
| # build error message within 24 lines we do the following: |
| # * filter sparse/smatch/cppcheck/coccinelle output |
| # * filter out 'CHECK drivers/blah.c' Kbuild lines |
| # * print 24 lines preceding the 'make[]: *** [] blah' pattern which |
| # make generates after an error |
| |
| sed -n '# Filter out useless stuff |
| /\[sparse\]$/d |
| /\[smatch\]$/d |
| /\[cppcheck\]$/d |
| /\[coccinelle\]$/d |
| /^ CHECK /d |
| # Add the line to the hold buffer |
| H |
| # If the line is the error marker, print out the entire hold |
| # buffer and quit |
| /^make\[.*\]: \*\*\* \[.*\]/ { g; p; q; } |
| # Do the same if the last line is reached |
| $ { g; p; q; }' | tail -n24 | |
| __print_build_log "$@" |
| } |
| |
| # Check if the build failed. |
| # Usage: build_failed <build_log> |
| build_failed() |
| { |
| local build_log="$1" |
| local failed |
| |
| failed="$(tail -n1 -- "$build_log")" |
| test "$failed" = "FAILURE" |
| } |
| |
| # Filter out the first element from a comma-separated list of elements. |
| # Usage: strip_first <list> |
| strip_first() |
| { |
| printf "%s" "$1" | sed -e 's/^[^,]*,\{0,1\}//g' |
| } |
| |
| # Filter out all but the first element from a comma-separated list of elements. |
| # Usage: leave_first <list> |
| leave_first() |
| { |
| printf "%s" "$1" | sed -e 's/,.*$//g' |
| } |
| |
| # Filter out all but the second element from a comma-separated list of elements. |
| # Usage: leave_second <list> |
| leave_second() |
| { |
| leave_first "$(strip_first "$1")" |
| } |
| |
| # Filter out all but the third element from a comma-separated list of elements. |
| # Usage: leave_third <list> |
| leave_third() |
| { |
| leave_second "$(strip_first "$1")" |
| } |
| |
| fi #__included_aiaiai_sh_functions |