yann@1: # This file contains some usefull common functions yann@1: # Copyright 2007 Yann E. MORIN yann@1: # Licensed under the GPL v2. See COPYING in the root of this package yann@1: yann@1: CT_OnError() { yann@1: ret=$? yann@1: CT_DoLog ERROR "Build failed in step \"${CT_STEP_MESSAGE[${CT_STEP_COUNT}]}\"" yann@1: for((step=(CT_STEP_COUNT-1); step>1; step--)); do yann@1: CT_DoLog ERROR " called in step \"${CT_STEP_MESSAGE[${step}]}\"" yann@1: done yann@1: CT_DoLog ERROR "Error happened in \"${BASH_SOURCE[1]}\" in function \"${FUNCNAME[1]}\" (line unknown, sorry)" yann@1: for((depth=2; ${BASH_LINENO[$((${depth}-1))]}>0; depth++)); do yann@1: CT_DoLog ERROR " called from \"${BASH_SOURCE[${depth}]}\" at line # ${BASH_LINENO[${depth}-1]} in function \"${FUNCNAME[${depth}]}\"" yann@1: done yann@1: CT_DoLog ERROR "Look at \"${CT_ACTUAL_LOG_FILE}\" for more info on this error." yann@1: exit $ret yann@1: } yann@1: trap CT_OnError ERR yann@1: yann@1: set -E yann@1: set -o pipefail yann@1: yann@1: # This is crosstool-ng-0.0.1 yann@1: CT_VERSION=ng-0.0.1 yann@1: yann@1: # The different log levels: yann@1: CT_LOG_LEVEL_ERROR=0 yann@1: CT_LOG_LEVEL_WARN=1 yann@1: CT_LOG_LEVEL_INFO=2 yann@1: CT_LOG_LEVEL_EXTRA=3 yann@1: CT_LOG_LEVEL_DEBUG=4 yann@1: yann@1: # Attributes yann@1: _A_NOR="\\033[0m" yann@1: _A_BRI="\\033[1m" yann@1: _A_DIM="\\033[2m" yann@1: _A_UND="\\033[4m" yann@1: _A_BRB="\\033[5m" yann@1: _A_REV="\\033[7m" yann@1: _A_HID="\\033[8m" yann@1: yann@1: # Fore colors yann@1: _F_BLK="\\033[30m" yann@1: _F_RED="\\033[31m" yann@1: _F_GRN="\\033[32m" yann@1: _F_YEL="\\033[33m" yann@1: _F_BLU="\\033[34m" yann@1: _F_MAG="\\033[35m" yann@1: _F_CYA="\\033[36m" yann@1: _F_WHI="\\033[37m" yann@1: yann@1: # A function to log what is happening yann@1: # Different log level are available: yann@1: # - ERROR: A serious, fatal error occurred yann@1: # - WARN: A non fatal, non serious error occurred, take your responsbility with the generated build yann@1: # - INFO: Informational messages yann@1: # - EXTRA: Extra informational messages yann@1: # - DEBUG: Debug messages yann@1: # Usage: CT_DoLog [message] yann@1: # If message is empty, then stdin will be logged. yann@1: CT_DoLog() { yann@47: local max_level LEVEL level cur_l cur_L yann@47: local l yann@1: eval max_level="\${CT_LOG_LEVEL_${CT_LOG_LEVEL_MAX}}" yann@1: # Set the maximum log level to DEBUG if we have none yann@1: [ -z ${max_level} ] && max_level=${CT_LOG_LEVEL_DEBUG} yann@1: yann@47: LEVEL="$1"; shift yann@1: eval level="\${CT_LOG_LEVEL_${LEVEL}}" yann@1: yann@1: if [ $# -eq 0 ]; then yann@1: cat - yann@1: else yann@1: echo "${1}" yann@1: fi |( IFS="\n" # We want the full lines, even leading spaces yann@1: cpt=0 yann@1: indent=$((2*CT_STEP_COUNT)) yann@1: while read line; do yann@47: case "${CT_LOG_SEE_TOOLS_WARN},${line}" in yann@47: y,*"warning:"*) cur_L=WARN; cur_l=${CT_LOG_LEVEL_WARN};; yann@47: *"error:"*) cur_L=ERROR; cur_l=${CT_LOG_LEVEL_ERROR};; yann@47: "make["?*"]:"*"Stop.") cur_L=ERROR; cur_l=${CT_LOG_LEVEL_ERROR};; yann@47: *) cur_L="${LEVEL}"; cur_l="${level}";; yann@47: esac yann@47: l="`printf \"[%-5s]%*s%s%s\" \"${cur_L}\" \"${indent}\" \" \" \"${line}\"`" yann@1: # There will always be a log file, be it /dev/null yann@1: echo -e "${l}" >>"${CT_ACTUAL_LOG_FILE}" yann@47: color="CT_${cur_L}_COLOR" yann@1: normal="CT_NORMAL_COLOR" yann@47: if [ ${cur_l} -le ${max_level} ]; then yann@1: echo -e "${!color}${l}${!normal}" yann@1: else yann@1: ${CT_PROG_BAR} yann@1: fi yann@1: done yann@1: ) yann@1: yann@1: return 0 yann@1: } yann@1: yann@1: # Abort the execution with a error message yann@1: # Usage: CT_Abort yann@1: CT_Abort() { yann@1: CT_DoLog ERROR "$1" >&2 yann@1: exit 1 yann@1: } yann@1: yann@1: # Test a condition, and print a message if satisfied yann@1: # Usage: CT_Test yann@1: CT_Test() { yann@1: local ret yann@1: local m="$1" yann@1: shift yann@1: test "$@" && CT_DoLog WARN "$m" yann@1: return 0 yann@1: } yann@1: yann@1: # Test a condition, and abort with an error message if satisfied yann@1: # Usage: CT_TestAndAbort yann@1: CT_TestAndAbort() { yann@1: local m="$1" yann@1: shift yann@1: test "$@" && CT_Abort "$m" yann@1: return 0 yann@1: } yann@1: yann@1: # Test a condition, and abort with an error message if not satisfied yann@1: # Usage: CT_TestAndAbort yann@1: CT_TestOrAbort() { yann@1: local m="$1" yann@1: shift yann@1: test "$@" || CT_Abort "$m" yann@1: return 0 yann@1: } yann@1: yann@1: # Test the presence of a tool, or abort if not found yann@1: # Usage: CT_HasOrAbort yann@1: CT_HasOrAbort() { yann@1: CT_TestAndAbort "\"${1}\" not found and needed for successfull toolchain build." -z "`which \"${1}\"`" yann@1: return 0 yann@1: } yann@1: yann@1: # Get current date with nanosecond precision yann@1: # On those system not supporting nanosecond precision, faked with rounding down yann@1: # to the highest entire second yann@1: # Usage: CT_DoDate yann@1: CT_DoDate() { yann@1: date "$1" |sed -r -e 's/%N$/000000000/;' yann@1: } yann@1: yann@1: CT_STEP_COUNT=1 yann@1: CT_STEP_MESSAGE[${CT_STEP_COUNT}]="" yann@1: # Memorise a step being done so that any error is caught yann@1: # Usage: CT_DoStep yann@1: CT_DoStep() { yann@1: local start=`CT_DoDate +%s%N` yann@1: CT_DoLog "$1" "=================================================================" yann@1: CT_DoLog "$1" "$2" yann@1: CT_STEP_COUNT=$((CT_STEP_COUNT+1)) yann@1: CT_STEP_LEVEL[${CT_STEP_COUNT}]="$1"; shift yann@1: CT_STEP_START[${CT_STEP_COUNT}]="${start}" yann@1: CT_STEP_MESSAGE[${CT_STEP_COUNT}]="$1" yann@1: return 0 yann@1: } yann@1: yann@1: # End the step just being done yann@1: # Usage: CT_EndStep yann@1: CT_EndStep() { yann@1: local stop=`CT_DoDate +%s%N` yann@1: local duration=`printf "%032d" $((stop-${CT_STEP_START[${CT_STEP_COUNT}]})) |sed -r -e 's/([[:digit:]]{2})[[:digit:]]{7}$/\.\1/; s/^0+//; s/^\./0\./;'` yann@1: local level="${CT_STEP_LEVEL[${CT_STEP_COUNT}]}" yann@1: local message="${CT_STEP_MESSAGE[${CT_STEP_COUNT}]}" yann@1: CT_STEP_COUNT=$((CT_STEP_COUNT-1)) yann@1: CT_DoLog "${level}" "${message}: done in ${duration}s" yann@1: return 0 yann@1: } yann@1: yann@1: # Pushes into a directory, and pops back yann@1: CT_Pushd() { yann@1: pushd "$1" >/dev/null 2>&1 yann@1: } yann@1: CT_Popd() { yann@1: popd >/dev/null 2>&1 yann@1: } yann@1: yann@1: # Makes a path absolute yann@1: # Usage: CT_MakeAbsolutePath path yann@1: CT_MakeAbsolutePath() { yann@1: # Try to cd in that directory yann@1: if [ -d "$1" ]; then yann@1: CT_Pushd "$1" yann@1: pwd yann@1: CT_Popd yann@1: else yann@1: # No such directory, fail back to guessing yann@1: case "$1" in yann@1: /*) echo "$1";; yann@1: *) echo "`pwd`/$1";; yann@1: esac yann@1: fi yann@1: yann@1: return 0 yann@1: } yann@1: yann@1: # Creates a temporary directory yann@1: # $1: variable to assign to yann@1: # Usage: CT_MktempDir foo yann@1: CT_MktempDir() { yann@1: # Some mktemp do not allow more than 6 Xs yann@1: eval "$1"="`mktemp -q -d \"${CT_BUILD_DIR}/.XXXXXX\"`" yann@1: CT_TestOrAbort "Could not make temporary directory" -n "${!1}" -a -d "${!1}" yann@1: } yann@1: yann@1: # Echoes the specified string on stdout until the pipe breaks. yann@1: # Doesn't fail yann@1: # $1: string to echo yann@1: # Usage: CT_DoYes "" |make oldconfig yann@1: CT_DoYes() { yann@1: yes "$1" || true yann@1: }