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@136: # Prepare the fault handler 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@112: [ "${CT_LOG_TO_FILE}" = "y" ] && CT_DoLog ERROR "Look at \"${CT_LOG_FILE}\" for more info on this error." yann@96: CT_STEP_COUNT=1 yann@96: CT_DoEnd ERROR yann@1: exit $ret yann@1: } yann@136: yann@136: # Install the fault handler yann@1: trap CT_OnError ERR yann@1: yann@136: # Inherit the fault handler in subshells and functions yann@1: set -E yann@136: yann@136: # Make pipes fail on the _first_ failed command yann@136: # Not supported on bash < 3.x, but we need it, so drop the obsoleting bash-2.x yann@1: set -o pipefail yann@1: yann@136: # Don't hash commands' locations, and search every time it is requested. yann@136: # This is slow, but needed because of the static/shared core gcc which shall yann@136: # always match to shared if it exists, and only fallback to static if the yann@136: # shared is not found yann@136: set +o hashall yann@136: yann@165: # Log policy: yann@165: # - first of all, save stdout so we can see the live logs: fd #6 yann@165: exec 6>&1 yann@165: # - then point stdout to the log file (temporary for now) yann@165: tmp_log_file="${CT_TOP_DIR}/log.$$" yann@165: exec >>"${tmp_log_file}" yann@165: 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@78: CT_LOG_LEVEL_ALL=5 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@78: # - ALL: Component's build 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@78: [ -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@141: fi |( IFS="\n" # We want the full lines, even leading spaces yann@82: CT_PROG_BAR_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@112: y,*"WARNING:"*) cur_L=WARN; cur_l=${CT_LOG_LEVEL_WARN};; yann@47: *"error:"*) cur_L=ERROR; cur_l=${CT_LOG_LEVEL_ERROR};; yann@78: *"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@112: echo -e "${l}" yann@47: if [ ${cur_l} -le ${max_level} ]; then yann@112: echo -e "\r${l}" >&6 yann@82: fi yann@82: if [ "${CT_LOG_PROGRESS_BAR}" = "y" ]; then yann@82: [ ${CT_PROG_BAR_CPT} -eq 0 ] && bar="/" yann@82: [ ${CT_PROG_BAR_CPT} -eq 10 ] && bar="-" yann@82: [ ${CT_PROG_BAR_CPT} -eq 20 ] && bar="\\" yann@82: [ ${CT_PROG_BAR_CPT} -eq 30 ] && bar="|" yann@141: printf "\r[%02d:%02d] %s " $((SECONDS/60)) $((SECONDS%60)) "${bar}" >&6 yann@82: CT_PROG_BAR_CPT=$(((CT_PROG_BAR_CPT+1)%40)) yann@1: fi yann@1: done yann@1: ) yann@1: yann@1: return 0 yann@1: } yann@1: yann@96: # Tail message to be logged whatever happens yann@96: # Usage: CT_DoEnd yann@96: CT_DoEnd() yann@96: { yann@145: local level="$1" yann@96: CT_STOP_DATE=`CT_DoDate +%s%N` yann@96: CT_STOP_DATE_HUMAN=`CT_DoDate +%Y%m%d.%H%M%S` yann@145: CT_DoLog "${level:-INFO}" "Build completed at ${CT_STOP_DATE_HUMAN}" yann@96: elapsed=$((CT_STOP_DATE-CT_STAR_DATE)) yann@96: elapsed_min=$((elapsed/(60*1000*1000*1000))) yann@96: elapsed_sec=`printf "%02d" $(((elapsed%(60*1000*1000*1000))/(1000*1000*1000)))` yann@96: elapsed_csec=`printf "%02d" $(((elapsed%(1000*1000*1000))/(10*1000*1000)))` yann@145: CT_DoLog ${level:-INFO} "(elapsed: ${elapsed_min}:${elapsed_sec}.${elapsed_csec})" yann@96: } yann@96: yann@96: # Abort the execution with an error message yann@1: # Usage: CT_Abort yann@1: CT_Abort() { yann@128: CT_DoLog ERROR "$1" 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: } yann@63: yann@85: # Get the file name extension of a component yann@85: # Usage: CT_GetFileExtension yann@85: # If found, echoes the extension to stdout yann@85: # If not found, echoes nothing on stdout. yann@85: CT_GetFileExtension() { yann@85: local ext yann@85: local file="$1" yann@85: local got_it=1 yann@85: yann@85: CT_Pushd "${CT_TARBALLS_DIR}" yann@160: # we need to also check for an empty extension for those very yann@160: # peculiar components that don't have one (such as sstrip from yann@160: # buildroot). yann@160: for ext in .tar.gz .tar.bz2 .tgz .tar ''; do yann@85: if [ -f "${file}${ext}" ]; then yann@85: echo "${ext}" yann@85: got_it=0 yann@85: break yann@85: fi yann@85: done yann@85: CT_Popd yann@85: yann@85: return 0 yann@85: } yann@85: yann@63: # Download an URL using wget yann@63: # Usage: CT_DoGetFileWget yann@63: CT_DoGetFileWget() { yann@63: # Need to return true because it is legitimate to not find the tarball at yann@63: # some of the provided URLs (think about snapshots, different layouts for yann@63: # different gcc versions, etc...) yann@63: # Some (very old!) FTP server might not support the passive mode, thus yann@63: # retry without yann@63: # With automated download as we are doing, it can be very dangerous to use yann@63: # -c to continue the downloads. It's far better to simply overwrite the yann@63: # destination file yann@63: wget -nc --progress=dot:binary --tries=3 --passive-ftp "$1" || wget -nc --progress=dot:binary --tries=3 "$1" || true yann@63: } yann@63: yann@63: # Download an URL using curl yann@63: # Usage: CT_DoGetFileCurl yann@63: CT_DoGetFileCurl() { yann@63: # Note: comments about wget method are also valid here yann@63: # Plus: no good progreess indicator is available with curl, yann@63: # so output is consigned to oblivion yann@63: curl --ftp-pasv -O --retry 3 "$1" >/dev/null || curl -O --retry 3 "$1" >/dev/null || true yann@63: } yann@63: yann@63: # Wrapper function to call one of curl or wget yann@63: # Usage: CT_DoGetFile yann@63: CT_DoGetFile() { yann@63: local _wget=`which wget` yann@63: local _curl=`which curl` yann@63: case "${_wget},${_curl}" in yann@63: ,) CT_DoError "Could find neither wget nor curl";; yann@146: ,*) CT_DoGetFileCurl "$1" 2>&1 |CT_DoLog ALL;; yann@146: *) CT_DoGetFileWget "$1" 2>&1 |CT_DoLog ALL;; yann@63: esac yann@63: } yann@63: yann@63: # Download the file from one of the URLs passed as argument yann@63: # Usage: CT_GetFile [ ...] yann@63: CT_GetFile() { yann@63: local got_it yann@63: local ext yann@63: local url yann@63: local file="$1" yann@63: shift yann@63: yann@63: # Do we already have it? yann@63: ext=`CT_GetFileExtension "${file}"` yann@63: if [ -n "${ext}" ]; then yann@85: CT_DoLog DEBUG "Already have \"${file}\"" yann@85: return 0 yann@63: fi yann@63: yann@63: CT_Pushd "${CT_TARBALLS_DIR}" yann@63: # File not yet downloaded, try to get it yann@63: got_it=0 yann@85: # We'd rather have a bzip2'ed tarball, then gzipped, and finally plain tar. yann@102: # Try local copy first, if it exists yann@85: for ext in .tar.bz2 .tar.gz .tgz .tar; do yann@102: if [ -r "${CT_LOCAL_TARBALLS_DIR}/${file}${ext}" -a \ yann@102: "${CT_FORCE_DOWNLOAD}" != "y" ]; then yann@107: CT_DoLog EXTRA "Copying \"${file}\" from local copy" yann@121: cp -v "${CT_LOCAL_TARBALLS_DIR}/${file}${ext}" "${file}${ext}" |CT_DoLog ALL yann@102: return 0 yann@63: fi yann@63: done yann@102: # Try to download it yann@107: CT_DoLog EXTRA "Retrieving \"${file}\"" yann@102: for ext in .tar.bz2 .tar.gz .tgz .tar; do yann@102: # Try all urls in turn yann@102: for url in "$@"; do yann@102: case "${url}" in yann@102: *) CT_DoLog DEBUG "Trying \"${url}/${file}${ext}\"" yann@102: CT_DoGetFile "${url}/${file}${ext}" yann@102: ;; yann@102: esac yann@102: [ -f "${file}${ext}" ] && return 0 || true yann@102: done yann@102: done yann@63: CT_Popd yann@63: yann@102: CT_Abort "Could not download \"${file}\", and not present in \"${CT_LOCAL_TARBALLS_DIR}\"" yann@63: } yann@63: yann@63: # Extract a tarball and patch the resulting sources if necessary. yann@63: # Some tarballs need to be extracted in specific places. Eg.: glibc addons yann@63: # must be extracted in the glibc directory; uCLibc locales must be extracted yann@63: # in the extra/locale sub-directory of uClibc. yann@63: CT_ExtractAndPatch() { yann@63: local file="$1" yann@63: local base_file=`echo "${file}" |cut -d - -f 1` yann@63: local ver_file=`echo "${file}" |cut -d - -f 2-` yann@63: local official_patch_dir yann@63: local custom_patch_dir yann@63: local libc_addon yann@63: local ext=`CT_GetFileExtension "${file}"` yann@63: CT_TestAndAbort "\"${file}\" not found in \"${CT_TARBALLS_DIR}\"" -z "${ext}" yann@63: local full_file="${CT_TARBALLS_DIR}/${file}${ext}" yann@63: yann@63: CT_Pushd "${CT_SRC_DIR}" yann@63: yann@63: # Add-ons need a little love, really. yann@63: case "${file}" in yann@63: glibc-[a-z]*-*) yann@63: CT_TestAndAbort "Trying to extract the C-library addon/locales \"${file}\" when C-library not yet extracted" ! -d "${CT_LIBC_FILE}" yann@63: cd "${CT_LIBC_FILE}" yann@63: libc_addon=y yann@63: [ -f ".${file}.extracted" ] && return 0 yann@63: touch ".${file}.extracted" yann@63: ;; yann@63: uClibc-locale-*) yann@63: CT_TestAndAbort "Trying to extract the C-library addon/locales \"${file}\" when C-library not yet extracted" ! -d "${CT_LIBC_FILE}" yann@63: cd "${CT_LIBC_FILE}/extra/locale" yann@63: libc_addon=y yann@63: [ -f ".${file}.extracted" ] && return 0 yann@63: touch ".${file}.extracted" yann@63: ;; yann@63: esac yann@63: yann@63: # If the directory exists, then consider extraction and patching done yann@77: if [ -d "${file}" ]; then yann@78: CT_DoLog DEBUG "Already extracted \"${file}\"" yann@77: return 0 yann@77: fi yann@63: yann@63: CT_DoLog EXTRA "Extracting \"${file}\"" yann@63: case "${ext}" in yann@78: .tar.bz2) tar xvjf "${full_file}" |CT_DoLog ALL;; yann@78: .tar.gz|.tgz) tar xvzf "${full_file}" |CT_DoLog ALL;; yann@78: .tar) tar xvf "${full_file}" |CT_DoLog ALL;; yann@63: *) CT_Abort "Don't know how to handle \"${file}\": unknown extension" ;; yann@63: esac yann@63: yann@63: # Snapshots might not have the version number in the extracted directory yann@63: # name. This is also the case for some (old) packages, such as libfloat. yann@63: # Overcome this issue by symlink'ing the directory. yann@63: if [ ! -d "${file}" -a "${libc_addon}" != "y" ]; then yann@63: case "${ext}" in yann@63: .tar.bz2) base=`tar tjf "${full_file}" |head -n 1 |cut -d / -f 1 || true`;; yann@63: .tar.gz|.tgz) base=`tar tzf "${full_file}" |head -n 1 |cut -d / -f 1 || true`;; yann@63: .tar) base=`tar tf "${full_file}" |head -n 1 |cut -d / -f 1 || true`;; yann@63: esac yann@63: CT_TestOrAbort "There was a problem when extracting \"${file}\"" -d "${base}" -o "${base}" != "${file}" yann@63: ln -s "${base}" "${file}" yann@63: fi yann@63: yann@63: # Kludge: outside this function, we wouldn't know if we had just extracted yann@63: # a libc addon, or a plain package. Apply patches now. yann@63: CT_DoLog EXTRA "Patching \"${file}\"" yann@63: yann@136: if [ "${libc_addon}" = "y" ]; then yann@136: # Some addons tarball directly contian the correct addon directory, yann@136: # while others have the addon directory named ofter the tarball. yann@136: # Fix that bu always using the short name (eg: linuxthreads, ports, etc...) yann@136: addon_short_name=`echo "${file}" |sed -r -e 's/^[^-]+-//; s/-[^-]+$//;'` yann@136: [ -d "${addon_short_name}" ] || ln -s "${file}" "${addon_short_name}" yann@136: # If libc addon, we're already in the correct place yann@136: else yann@136: cd "${file}" yann@136: fi yann@63: yann@63: [ "${CUSTOM_PATCH_ONLY}" = "y" ] || official_patch_dir="${CT_TOP_DIR}/patches/${base_file}/${ver_file}" yann@63: [ "${CT_CUSTOM_PATCH}" = "y" ] && custom_patch_dir="${CT_CUSTOM_PATCH_DIR}/${base_file}/${ver_file}" yann@63: for patch_dir in "${official_patch_dir}" "${custom_patch_dir}"; do yann@63: if [ -n "${patch_dir}" -a -d "${patch_dir}" ]; then yann@63: for p in "${patch_dir}"/*.patch; do yann@63: if [ -f "${p}" ]; then yann@63: CT_DoLog DEBUG "Applying patch \"${p}\"" yann@78: patch -g0 -F1 -p1 -f <"${p}" |CT_DoLog ALL yann@63: CT_TestAndAbort "Failed while applying patch file \"${p}\"" ${PIPESTATUS[0]} -ne 0 yann@63: fi yann@63: done yann@63: fi yann@63: done yann@63: yann@63: CT_Popd yann@63: } yann@63: yann@63: # Compute the target triplet from what is provided by the user yann@63: # Usage: CT_DoBuildTargetTriplet yann@63: # In fact this function takes the environment variables to build the target yann@63: # triplet. It is needed both by the normal build sequence, as well as the yann@63: # sample saving sequence. yann@63: CT_DoBuildTargetTriplet() { yann@63: case "${CT_ARCH_BE},${CT_ARCH_LE}" in yann@63: y,) target_endian_eb=eb; target_endian_el=;; yann@63: ,y) target_endian_eb=; target_endian_el=el;; yann@63: esac yann@63: case "${CT_ARCH}" in yann@63: arm) CT_TARGET="${CT_ARCH}${target_endian_eb}";; yann@63: mips) CT_TARGET="${CT_ARCH}${target_endian_el}";; yann@63: x86*) # Much love for this one :-( yann@63: arch="${CT_ARCH_ARCH}" yann@63: [ -z "${arch}" ] && arch="${CT_ARCH_TUNE}" yann@63: case "${CT_ARCH}" in yann@127: x86_64) CT_TARGET=x86_64;; yann@63: *) case "${arch}" in yann@63: "") CT_TARGET=i386;; yann@63: i386|i486|i586|i686) CT_TARGET="${arch}";; yann@63: winchip*) CT_TARGET=i486;; yann@63: pentium|pentium-mmx|c3*) CT_TARGET=i586;; yann@63: nocona|athlon*64|k8|athlon-fx|opteron) CT_TARGET=x86_64;; yann@63: pentiumpro|pentium*|athlon*) CT_TARGET=i686;; yann@63: *) CT_TARGET=i586;; yann@63: esac;; yann@63: esac;; yann@63: esac yann@63: case "${CT_TARGET_VENDOR}" in yann@63: "") CT_TARGET="${CT_TARGET}-unknown";; yann@63: *) CT_TARGET="${CT_TARGET}-${CT_TARGET_VENDOR}";; yann@63: esac yann@63: case "${CT_KERNEL}" in yann@63: linux*) CT_TARGET="${CT_TARGET}-linux";; yann@63: esac yann@63: case "${CT_LIBC}" in yann@63: glibc) CT_TARGET="${CT_TARGET}-gnu";; yann@63: uClibc) CT_TARGET="${CT_TARGET}-uclibc";; yann@63: esac yann@63: CT_TARGET="`${CT_TOP_DIR}/tools/config.sub ${CT_TARGET}`" yann@63: } yann@121: yann@121: # This function does pause the build until the user strikes "Return" yann@121: # Usage: CT_DoPause [optional_message] yann@121: CT_DoPause() { yann@121: local foo yann@121: local message="${1:-Pausing for your pleasure}" yann@121: CT_DoLog INFO "${message}" yann@121: read -p "Press \"Enter\" to continue, or Ctrl-C to stop..." foo >&6 yann@121: return 0 yann@121: } yann@121: yann@121: # This function saves the state of the toolchain to be able to restart yann@121: # at any one point yann@121: # Usage: CT_DoSaveState yann@121: CT_DoSaveState() { yann@121: [ "${CT_DEBUG_CT_SAVE_STEPS}" = "y" ] || return 0 yann@121: local state_name="$1" yann@121: local state_dir="${CT_STATE_DIR}/${state_name}" yann@121: yann@121: CT_DoLog DEBUG "Saving state to restart at step \"${state_name}\"..." yann@121: rm -rf "${state_dir}" yann@121: mkdir -p "${state_dir}" yann@121: yann@121: case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in yann@121: y) tar_opt=czf; tar_ext=".tar.gz";; yann@121: *) tar_opt=cf; tar_ext=".tar";; yann@121: esac yann@121: yann@121: CT_DoLog DEBUG " Saving environment and aliases" yann@121: # We must omit shell functions yann@121: # 'isgrep' is here because I don't seem to yann@121: # be able to remove the functions names. yann@121: set |awk ' yann@121: BEGIN { _p = 1; } yann@121: $0~/^[^ ] ()/ { _p = 0; } yann@121: _p == 1 yann@121: $0 == "}" { _p = 1; } yann@121: ' |egrep -v '^[^ ]+ \(\)' >"${state_dir}/env.sh" yann@121: yann@136: CT_DoLog DEBUG " Saving CT_CC_CORE_STATIC_PREFIX_DIR=\"${CT_CC_CORE_STATIC_PREFIX_DIR}\"" yann@136: CT_Pushd "${CT_CC_CORE_STATIC_PREFIX_DIR}" yann@136: tar ${tar_opt} "${state_dir}/cc_core_static_prefix_dir${tar_ext}" . yann@136: CT_Popd yann@136: yann@136: CT_DoLog DEBUG " Saving CT_CC_CORE_SHARED_PREFIX_DIR=\"${CT_CC_CORE_SHARED_PREFIX_DIR}\"" yann@136: CT_Pushd "${CT_CC_CORE_SHARED_PREFIX_DIR}" yann@136: tar ${tar_opt} "${state_dir}/cc_core_shared_prefix_dir${tar_ext}" . yann@121: CT_Popd yann@121: yann@121: CT_DoLog DEBUG " Saving CT_PREFIX_DIR=\"${CT_PREFIX_DIR}\"" yann@121: CT_Pushd "${CT_PREFIX_DIR}" yann@136: tar ${tar_opt} "${state_dir}/prefix_dir${tar_ext}" --exclude '*.log' . yann@121: CT_Popd yann@121: yann@121: if [ "${CT_LOG_TO_FILE}" = "y" ]; then yann@121: CT_DoLog DEBUG " Saving log file" yann@121: exec >/dev/null yann@121: case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in yann@121: y) gzip -3 -c "${CT_LOG_FILE}" >"${state_dir}/log.gz";; yann@121: *) cat "${CT_LOG_FILE}" >"${state_dir}/log";; yann@121: esac yann@121: exec >>"${CT_LOG_FILE}" yann@121: fi yann@121: } yann@121: yann@136: # This function restores a previously saved state yann@121: # Usage: CT_DoLoadState yann@121: CT_DoLoadState(){ yann@121: local state_name="$1" yann@121: local state_dir="${CT_STATE_DIR}/${state_name}" yann@135: local old_RESTART="${CT_RESTART}" yann@135: local old_STOP="${CT_STOP}" yann@121: yann@141: CT_TestOrAbort "The previous build did not reach the point where it could be restarted at \"${CT_RESTART}\"" -d "${state_dir}" yann@141: yann@121: # We need to do something special with the log file! yann@121: if [ "${CT_LOG_TO_FILE}" = "y" ]; then yann@121: exec >"${state_dir}/tail.log" yann@121: fi yann@135: CT_DoLog INFO "Restoring state at step \"${state_name}\", as requested." yann@121: yann@121: case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in yann@121: y) tar_opt=xzf; tar_ext=".tar.gz";; yann@121: *) tar_opt=cf; tar_ext=".tar";; yann@121: esac yann@121: yann@121: CT_DoLog DEBUG " Removing previous build directories" yann@136: chmod -R u+rwX "${CT_PREFIX_DIR}" "${CT_CC_CORE_SHARED_PREFIX_DIR}" "${CT_CC_CORE_STATIC_PREFIX_DIR}" yann@136: rm -rf "${CT_PREFIX_DIR}" "${CT_CC_CORE_SHARED_PREFIX_DIR}" "${CT_CC_CORE_STATIC_PREFIX_DIR}" yann@136: mkdir -p "${CT_PREFIX_DIR}" "${CT_CC_CORE_SHARED_PREFIX_DIR}" "${CT_CC_CORE_STATIC_PREFIX_DIR}" yann@121: yann@121: CT_DoLog DEBUG " Restoring CT_PREFIX_DIR=\"${CT_PREFIX_DIR}\"" yann@121: CT_Pushd "${CT_PREFIX_DIR}" yann@121: tar ${tar_opt} "${state_dir}/prefix_dir${tar_ext}" yann@121: CT_Popd yann@121: yann@136: CT_DoLog DEBUG " Restoring CT_CC_CORE_SHARED_PREFIX_DIR=\"${CT_CC_CORE_SHARED_PREFIX_DIR}\"" yann@136: CT_Pushd "${CT_CC_CORE_SHARED_PREFIX_DIR}" yann@136: tar ${tar_opt} "${state_dir}/cc_core_shared_prefix_dir${tar_ext}" yann@136: CT_Popd yann@136: yann@136: CT_DoLog DEBUG " Restoring CT_CC_CORE_STATIC_PREFIX_DIR=\"${CT_CC_CORE_STATIC_PREFIX_DIR}\"" yann@136: CT_Pushd "${CT_CC_CORE_STATIC_PREFIX_DIR}" yann@136: tar ${tar_opt} "${state_dir}/cc_core_static_prefix_dir${tar_ext}" yann@121: CT_Popd yann@121: yann@121: # Restore the environment, discarding any error message yann@121: # (for example, read-only bash internals) yann@121: CT_DoLog DEBUG " Restoring environment" yann@121: . "${state_dir}/env.sh" >/dev/null 2>&1 || true yann@121: yann@135: # Restore the new RESTART and STOP steps yann@135: CT_RESTART="${old_RESTART}" yann@135: CT_STOP="${old_STOP}" yann@135: unset old_stop old_restart yann@135: yann@121: if [ "${CT_LOG_TO_FILE}" = "y" ]; then yann@121: CT_DoLog DEBUG " Restoring log file" yann@121: exec >/dev/null yann@121: case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in yann@121: y) zcat "${state_dir}/log.gz" >"${CT_LOG_FILE}";; yann@121: *) cat "${state_dir}/log" >"${CT_LOG_FILE}";; yann@121: esac yann@121: cat "${state_dir}/tail.log" >>"${CT_LOG_FILE}" yann@121: exec >>"${CT_LOG_FILE}" yann@121: rm -f "${state_dir}/tail.log" yann@121: fi yann@121: }