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@726: # Bail out early in subshell, the upper level shell will act accordingly. yann@726: [ ${BASH_SUBSHELL} -eq 0 ] || exit $ret yann@523: 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@523: CT_DoLog ERROR " called in step '${CT_STEP_MESSAGE[${step}]}'" yann@1: done yann@523: 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@523: CT_DoLog ERROR " called from '${BASH_SOURCE[${depth}]}' at line # ${BASH_LINENO[${depth}-1]} in function '${FUNCNAME[${depth}]}'" yann@1: done yann@523: [ "${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@1741: CT_LOG_LEVEL_ALL=4 yann@1741: CT_LOG_LEVEL_DEBUG=5 yann@1: yann@1083: # Make it easy to use \n and ! yann@1081: CR=$(printf "\n") yann@1083: BANG='!' yann@1081: 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@1516: printf "${*}\n" yann@1081: fi |( IFS="${CR}" # We want the full lines, even leading spaces yann@523: _prog_bar_cpt=0 yann@523: _prog_bar[0]='/' yann@523: _prog_bar[1]='-' yann@523: _prog_bar[2]='\' yann@523: _prog_bar[3]='|' 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@919: *"make["*"]: *** ["*) cur_L=ERROR; cur_l=${CT_LOG_LEVEL_ERROR};; yann@47: *) cur_L="${LEVEL}"; cur_l="${level}";; yann@47: esac yann@523: # There will always be a log file (stdout, fd #1), be it /dev/null yann@523: printf "[%-5s]%*s%s%s\n" "${cur_L}" "${indent}" " " "${line}" yann@47: if [ ${cur_l} -le ${max_level} ]; then yann@523: # Only print to console (fd #6) if log level is high enough. yann@523: printf "\r[%-5s]%*s%s%s\n" "${cur_L}" "${indent}" " " "${line}" >&6 yann@82: fi yann@82: if [ "${CT_LOG_PROGRESS_BAR}" = "y" ]; then yann@523: printf "\r[%02d:%02d] %s " $((SECONDS/60)) $((SECONDS%60)) "${_prog_bar[$((_prog_bar_cpt/10))]}" >&6 yann@523: _prog_bar_cpt=$(((_prog_bar_cpt+1)%40)) yann@1: fi yann@1: done yann@1: ) yann@1: yann@1: return 0 yann@1: } yann@1: yann@535: # Execute an action, and log its messages yann@1257: # Usage: [VAR=val...] CT_DoExecLog yann@535: CT_DoExecLog() { yann@535: local level="$1" yann@535: shift yann@1516: CT_DoLog DEBUG "==> Executing: '${*}'" yann@668: "${@}" 2>&1 |CT_DoLog "${level}" yann@535: } yann@535: 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@523: CT_STOP_DATE=$(CT_DoDate +%s%N) yann@523: CT_STOP_DATE_HUMAN=$(CT_DoDate +%Y%m%d.%H%M%S) yann@599: if [ "${level}" != "ERROR" ]; then yann@582: CT_DoLog "${level:-INFO}" "Build completed at ${CT_STOP_DATE_HUMAN}" yann@582: fi yann@96: elapsed=$((CT_STOP_DATE-CT_STAR_DATE)) yann@96: elapsed_min=$((elapsed/(60*1000*1000*1000))) yann@523: elapsed_sec=$(printf "%02d" $(((elapsed%(60*1000*1000*1000))/(1000*1000*1000)))) yann@523: 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@773: CT_TestAndAbort "'${1}' not found and needed for successful toolchain build." -z "$(CT_Which "${1}")" yann@1: return 0 yann@1: } yann@1: yann@210: # Search a program: wrap "which" for those system where yann@1226: # "which" verbosely says there is no match (Mandriva is yann@1226: # such a sucker...) yann@210: # Usage: CT_Which yann@210: CT_Which() { yann@210: which "$1" 2>/dev/null || true yann@210: } yann@210: 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@523: 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@523: local stop=$(CT_DoDate +%s%N) yann@523: 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@582: local elapsed=$(printf "%02d:%02d" $((SECONDS/60)) $((SECONDS%60))) 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@582: CT_DoLog "${level}" "${message}: done in ${duration}s (at ${elapsed})" 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: # 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@1113: eval "$1"=$(mktemp -q -d "${CT_BUILD_DIR}/tmp.XXXXXX") yann@1: CT_TestOrAbort "Could not make temporary directory" -n "${!1}" -a -d "${!1}" yann@787: CT_DoLog DEBUG "Made temporary directory '${!1}'" yann@787: return 0 yann@1: } yann@1: yann@1148: # Removes one or more directories, even if it is read-only, or its parent is yann@1138: # Usage: CT_DoForceRmdir dir [...] yann@1138: CT_DoForceRmdir() { yann@1148: local dir yann@1148: local mode yann@1148: for dir in "${@}"; do yann@1148: [ -d "${dir}" ] || continue yann@1185: mode="$(stat -c '%a' "$(dirname "${dir}")")" yann@1185: CT_DoExecLog ALL chmod u+w "$(dirname "${dir}")" yann@1185: CT_DoExecLog ALL chmod -R u+w "${dir}" yann@1148: CT_DoExecLog ALL rm -rf "${dir}" yann@1185: CT_DoExecLog ALL chmod ${mode} "$(dirname "${dir}")" yann@1148: done yann@1138: } yann@1138: 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@1391: # Add the specified directory to LD_LIBRARY_PATH, and export it yann@1391: # If the specified patch is already present, just export yann@1391: # $1: path to add yann@1391: # $2: add as 'first' or 'last' path, 'first' is assumed if $2 is empty yann@1391: # Usage CT_SetLibPath /some/where/lib [first|last] yann@1391: CT_SetLibPath() { yann@1391: local path="$1" yann@1391: local pos="$2" yann@1391: yann@1391: case ":${LD_LIBRARY_PATH}:" in yann@1391: *:"${path}":*) ;; yann@1391: *) case "${pos}" in yann@1391: last) yann@1391: CT_DoLog DEBUG "Adding '${path}' at end of LD_LIBRARY_PATH" yann@1391: LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}${path}" yann@1391: ;; yann@1391: first|"") yann@1391: CT_DoLog DEBUG "Adding '${path}' at start of LD_LIBRARY_PATH" yann@1391: LD_LIBRARY_PATH="${path}${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}" yann@1391: ;; yann@1391: *) yann@1391: CT_Abort "Incorrect position '${pos}' to add '${path}' to LD_LIBRARY_PATH" yann@1391: ;; yann@1391: esac yann@1391: ;; yann@1391: esac yann@1391: CT_DoLog DEBUG "==> LD_LIBRARY_PATH='${LD_LIBRARY_PATH}'" yann@1391: export LD_LIBRARY_PATH yann@1391: } yann@1391: yann@85: # Get the file name extension of a component yann@719: # Usage: CT_GetFileExtension [extension] yann@1690: # If found, echoes the extension to stdout, and return 0 yann@1690: # If not found, echoes nothing on stdout, and return !0. yann@85: CT_GetFileExtension() { yann@85: local ext yann@85: local file="$1" yann@719: shift yann@719: local first_ext="$1" yann@85: 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@1763: for ext in ${first_ext} .tar.gz .tar.bz2 .tgz .tar /.git ''; do yann@1763: if [ -e "${CT_TARBALLS_DIR}/${file}${ext}" ]; then yann@85: echo "${ext}" yann@1690: exit 0 yann@85: fi yann@85: done yann@85: yann@1690: exit 1 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@492: # Some company networks have firewalls to connect to the internet, but it's yann@1113: # not easy to detect them, and wget does not timeout by default while yann@492: # connecting, so force a global ${CT_CONNECT_TIMEOUT}-second timeout. yann@1257: CT_DoExecLog ALL wget -T ${CT_CONNECT_TIMEOUT} -nc --progress=dot:binary --tries=3 --passive-ftp "$1" \ yann@1257: || CT_DoExecLog ALL wget -T ${CT_CONNECT_TIMEOUT} -nc --progress=dot:binary --tries=3 "$1" \ yann@439: || true yann@63: } yann@63: yann@63: # Download an URL using curl yann@63: # Usage: CT_DoGetFileCurl yann@63: CT_DoGetFileCurl() { yann@492: # Note: comments about wget method (above) are also valid here yann@439: # Plus: no good progress indicator is available with curl, yann@1257: # so, be silent. richard@1720: CT_DoExecLog ALL curl -s --ftp-pasv -O --retry 3 "$1" --connect-timeout ${CT_CONNECT_TIMEOUT} -L -f \ richard@1720: || CT_DoExecLog ALL curl -s -O --retry 3 "$1" --connect-timeout ${CT_CONNECT_TIMEOUT} -L -f \ yann@439: || true yann@63: } yann@63: yann@1669: # Download using aria2 yann@1669: # Usage: CT_DoGetFileAria2 yann@1669: CT_DoGetFileAria2() { yann@1669: # Note: comments about curl method (above) are also valid here yann@1669: # Plus: default progress indicator is a single line, so use verbose log yann@1669: # so that the CT-NG's ouput is 'live'. yann@1680: CT_DoExecLog ALL aria2c --summary-interval=1 -s ${CT_DOWNLOAD_MAX_CHUNKS} -m 3 -t ${CT_CONNECT_TIMEOUT} -p "$1" \ yann@1680: || CT_DoExecLog ALL aria2c --summary-interval=1 -s ${CT_DOWNLOAD_MAX_CHUNKS} -m 3 -t ${CT_CONNECT_TIMEOUT} "$1" \ yann@1673: || rm -f "${1##*/}" yann@1669: } yann@1669: yann@1669: # OK, just look if we have them... yann@1669: _aria2c=$(CT_Which aria2c) yann@523: _wget=$(CT_Which wget) yann@523: _curl=$(CT_Which curl) yann@1669: yann@1669: # Wrapper function to call one of, in order of preference: yann@1669: # aria2 yann@1669: # curl yann@1669: # wget yann@63: # Usage: CT_DoGetFile yann@63: CT_DoGetFile() { yann@1669: if [ -n "${_aria2c}" ]; then yann@1669: CT_DoGetFileAria2 "$1" yann@1669: elif [ -n "${_curl}" ]; then yann@1668: CT_DoGetFileCurl "$1" yann@1668: elif [ -n "${_wget}" ]; then yann@1668: CT_DoGetFileWget "$1" yann@1668: else yann@1668: CT_Abort "Could find neither wget nor curl" yann@1668: fi yann@63: } yann@63: yann@1113: # This function tries to retrieve a tarball form a local directory yann@1113: # Usage: CT_GetLocal [.extension] yann@1113: CT_GetLocal() { yann@1113: local basename="$1" yann@1113: local first_ext="$2" yann@1113: local ext yann@1113: yann@1113: # Do we already have it in *our* tarballs dir? yann@1690: if ext="$( CT_GetFileExtension "${basename}" ${first_ext} )"; then yann@1113: CT_DoLog DEBUG "Already have '${basename}'" yann@1113: return 0 yann@1113: fi yann@1113: yann@1113: if [ -n "${CT_LOCAL_TARBALLS_DIR}" ]; then yann@1113: CT_DoLog DEBUG "Trying to retrieve an already downloaded copy of '${basename}'" yann@1113: # We'd rather have a bzip2'ed tarball, then gzipped tarball, plain tarball, yann@1113: # or, as a failover, a file without extension. yann@1113: for ext in ${first_ext} .tar.bz2 .tar.gz .tgz .tar ''; do yann@1113: CT_DoLog DEBUG "Trying '${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}'" yann@1113: if [ -r "${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}" -a \ yann@1113: "${CT_FORCE_DOWNLOAD}" != "y" ]; then yann@1113: CT_DoLog DEBUG "Got '${basename}' from local storage" yann@1113: CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}" "${CT_TARBALLS_DIR}/${basename}${ext}" yann@1113: return 0 yann@1113: fi yann@1113: done yann@1113: fi yann@1113: return 1 yann@1113: } yann@1113: yann@1113: # This function saves the specified to local storage if possible, yann@1113: # and if so, symlinks it for later usage yann@1113: # Usage: CT_SaveLocal yann@1113: CT_SaveLocal() { yann@1113: local file="$1" yann@1113: local basename="${file##*/}" yann@1113: yann@1113: if [ "${CT_SAVE_TARBALLS}" = "y" ]; then yann@1134: CT_DoLog EXTRA "Saving '${basename}' to local storage" yann@1113: # The file may already exist if downloads are forced: remove it first yann@1113: CT_DoExecLog ALL rm -f "${CT_LOCAL_TARBALLS_DIR}/${basename}" yann@1113: CT_DoExecLog ALL mv -f "${file}" "${CT_LOCAL_TARBALLS_DIR}" yann@1113: CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${basename}" "${file}" yann@1113: fi yann@1113: } yann@1113: yann@63: # Download the file from one of the URLs passed as argument yann@1113: # Usage: CT_GetFile [.extension] [url ...] yann@63: CT_GetFile() { yann@63: local ext yann@1179: local url URLS LAN_URLS yann@63: local file="$1" yann@1113: local first_ext yann@63: shift yann@712: # If next argument starts with a dot, then this is not an URL, yann@712: # and we can consider that it is a preferred extension. yann@242: case "$1" in yann@712: .*) first_ext="$1" yann@242: shift yann@242: ;; yann@242: esac yann@63: yann@1113: # Does it exist localy? yann@1113: CT_GetLocal "${file}" ${first_ext} && return 0 || true yann@1113: # No, it does not... yann@63: yann@754: # Try to retrieve the file yann@788: CT_DoLog EXTRA "Retrieving '${file}'" yann@63: CT_Pushd "${CT_TARBALLS_DIR}" yann@754: yann@1179: URLS="$@" yann@1179: yann@1022: # Add URLs on the LAN mirror yann@1022: LAN_URLS= yann@1022: if [ "${CT_USE_MIRROR}" = "y" ]; then yann@1294: CT_TestOrAbort "Please set the mirror base URL" -n "${CT_MIRROR_BASE_URL}" yann@1294: LAN_URLS="${LAN_URLS} ${CT_MIRROR_BASE_URL}/${file%-*}" yann@1294: LAN_URLS="${LAN_URLS} ${CT_MIRROR_BASE_URL}" yann@695: yann@1134: if [ "${CT_PREFER_MIRROR}" = "y" ]; then yann@1134: CT_DoLog DEBUG "Pre-pending LAN mirror URLs" yann@1179: URLS="${LAN_URLS} ${URLS}" yann@1134: else yann@1134: CT_DoLog DEBUG "Appending LAN mirror URLs" yann@1179: URLS="${URLS} ${LAN_URLS}" yann@1134: fi yann@1022: fi yann@1022: yann@1022: # Scan all URLs in turn, and try to grab a tarball from there yann@1763: # Do *not* try git trees (ext=/.git), this is handled in a specific yann@1763: # wrapper, below yann@242: for ext in ${first_ext} .tar.bz2 .tar.gz .tgz .tar ''; do yann@102: # Try all urls in turn yann@1022: for url in ${URLS}; do yann@523: CT_DoLog DEBUG "Trying '${url}/${file}${ext}'" yann@265: CT_DoGetFile "${url}/${file}${ext}" yann@265: if [ -f "${file}${ext}" ]; then yann@799: CT_DoLog DEBUG "Got '${file}' from the Internet" yann@1113: CT_SaveLocal "${CT_TARBALLS_DIR}/${file}${ext}" yann@265: return 0 yann@265: fi yann@102: done yann@102: done yann@63: CT_Popd yann@63: yann@754: CT_Abort "Could not retrieve '${file}'." yann@63: } yann@63: yann@1113: # Checkout from CVS, and build the associated tarball yann@1113: # The tarball will be called ${basename}.tar.bz2 yann@1113: # Prerequisite: either the server does not require password, yann@1113: # or the user must already be logged in. yann@1113: # 'tag' is the tag to retrieve. Must be specified, but can be empty. yann@1113: # If dirname is specified, then module will be renamed to dirname yann@1113: # prior to building the tarball. yann@1592: # Usage: CT_GetCVS [dirname[=subdir]] yann@1592: # Note: if '=subdir' is given, then it is used instead of 'module'. yann@1113: CT_GetCVS() { yann@1113: local basename="$1" yann@1113: local uri="$2" yann@1113: local module="$3" yann@1113: local tag="${4:+-r ${4}}" yann@1113: local dirname="$5" yann@1113: local tmp_dir yann@1113: yann@1113: # Does it exist localy? yann@1113: CT_GetLocal "${basename}" && return 0 || true yann@1113: # No, it does not... yann@1113: yann@1113: CT_DoLog EXTRA "Retrieving '${basename}'" yann@1113: yann@1113: CT_MktempDir tmp_dir yann@1113: CT_Pushd "${tmp_dir}" yann@1113: yann@1113: CT_DoExecLog ALL cvs -z 9 -d "${uri}" co -P ${tag} "${module}" yann@1592: if [ -n "${dirname}" ]; then yann@1592: case "${dirname}" in yann@1592: *=*) yann@1593: CT_DoExecLog DEBUG mv "${dirname#*=}" "${dirname%%=*}" yann@1593: CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${dirname%%=*}" yann@1592: ;; yann@1592: *) yann@1592: CT_DoExecLog ALL mv "${module}" "${dirname}" yann@1592: CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${dirname:-${module}}" yann@1592: ;; yann@1592: esac yann@1592: fi yann@1113: CT_SaveLocal "${CT_TARBALLS_DIR}/${basename}.tar.bz2" yann@1113: yann@1113: CT_Popd yann@1113: CT_DoExecLog ALL rm -rf "${tmp_dir}" yann@1113: } yann@1113: yann@1244: # Check out from SVN, and build the associated tarball yann@1244: # The tarball will be called ${basename}.tar.bz2 yann@1244: # Prerequisite: either the server does not require password, yann@1244: # or the user must already be logged in. yann@1244: # 'rev' is the revision to retrieve yann@1244: # Usage: CT_GetSVN [rev] yann@1244: CT_GetSVN() { yann@1244: local basename="$1" yann@1244: local uri="$2" yann@1244: local rev="$3" yann@1244: yann@1244: # Does it exist localy? yann@1244: CT_GetLocal "${basename}" && return 0 || true yann@1244: # No, it does not... yann@1244: yann@1244: CT_DoLog EXTRA "Retrieving '${basename}'" yann@1244: yann@1244: CT_MktempDir tmp_dir yann@1244: CT_Pushd "${tmp_dir}" yann@1244: yann@1244: CT_DoExecLog ALL svn export ${rev:+-r ${rev}} "${uri}" "${basename}" yann@1244: CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${basename}" yann@1244: CT_SaveLocal "${CT_TARBALLS_DIR}/${basename}.tar.bz2" yann@1244: yann@1244: CT_Popd yann@1244: CT_DoExecLog ALL rm -rf "${tmp_dir}" yann@1244: } yann@1244: yann@1763: # Clone a git tree yann@1763: # Tries the given URLs in turn until one can get cloned. No tarball will be created. yann@1763: # Prerequisites: either the server does not require password, yann@1763: # or the user has already taken any action to authenticate to the server. yann@1763: # The cloned tree will *not* be stored in the local tarballs dir! yann@1763: # Usage: CT_GetGit yann@1763: CT_GetGit() { yann@1763: local basename="$1"; shift yann@1763: local url yann@1763: local cloned=0 yann@1763: yann@1763: # Do we have it in our tarballs dir? yann@1763: if [ -d "${CT_TARBALLS_DIR}/${basename}/.git" ]; then yann@1763: CT_DoLog EXTRA "Updating git tree '${basename}'" yann@1763: CT_Pushd "${CT_TARBALLS_DIR}/${basename}" yann@1763: CT_DoExecLog ALL git pull yann@1763: CT_Popd yann@1763: else yann@1763: CT_DoLog EXTRA "Retrieving git tree '${basename}'" yann@1763: for url in "${@}"; do yann@1763: CT_DoLog ALL "Trying to clone from '${url}'" yann@1763: CT_DoForceRmdir "${CT_TARBALLS_DIR}/${basename}" yann@1763: if git clone "${url}" "${CT_TARBALLS_DIR}/${basename}" 2>&1 |CT_DoLog ALL; then yann@1763: cloned=1 yann@1763: break yann@1763: fi yann@1763: done yann@1763: CT_TestOrAbort "Could not clone '${basename}'" ${cloned} -ne 0 yann@1763: fi yann@1763: } yann@1763: yann@1126: # Extract a tarball 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@1123: # in the extra/locale sub-directory of uClibc. This is taken into account yann@1123: # by the caller, that did a 'cd' into the correct path before calling us yann@1123: # and sets nochdir to 'nochdir'. yann@1763: # Note also that this function handles the git trees! yann@1763: # Usage: CT_Extract [nochdir] [options] yann@1763: # where 'options' are dependent on the source (eg. git branch/tag...) yann@1126: CT_Extract() { yann@1761: local nochdir="$1" yann@1761: local basename yann@1690: local ext yann@1690: yann@1761: if [ "${nochdir}" = "nochdir" ]; then yann@1761: shift yann@1761: nochdir="$(pwd)" yann@1761: else yann@1761: nochdir="${CT_SRC_DIR}" yann@1761: fi yann@1761: yann@1761: basename="$1" yann@1761: shift yann@1761: yann@1690: if ! ext="$(CT_GetFileExtension "${basename}")"; then yann@1763: CT_Abort "'${basename}' not found in '${CT_TARBALLS_DIR}'" yann@1690: fi yann@1126: local full_file="${CT_TARBALLS_DIR}/${basename}${ext}" yann@1126: yann@1718: # Check if already extracted yann@1718: if [ -e "${CT_SRC_DIR}/.${basename}.extracted" ]; then yann@1718: CT_DoLog DEBUG "Already extracted '${basename}'" yann@1718: return 0 yann@1718: fi yann@1718: yann@1691: # Check if previously partially extracted yann@1691: if [ -e "${CT_SRC_DIR}/.${basename}.extracting" ]; then yann@1691: CT_DoLog ERROR "The '${basename}' sources were partially extracted." yann@1691: CT_DoLog ERROR "Please remove first:" yann@1691: CT_DoLog ERROR " - the source dir for '${basename}', in '${CT_SRC_DIR}'" yann@1691: CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${basename}.extracting'" yann@1691: CT_Abort "I'll stop now to avoid any carnage..." yann@1691: fi yann@1691: CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${basename}.extracting" yann@1691: yann@1761: CT_Pushd "${nochdir}" yann@63: yann@1126: CT_DoLog EXTRA "Extracting '${basename}'" yann@63: case "${ext}" in yann@776: .tar.bz2) CT_DoExecLog ALL tar xvjf "${full_file}";; yann@776: .tar.gz|.tgz) CT_DoExecLog ALL tar xvzf "${full_file}";; yann@776: .tar) CT_DoExecLog ALL tar xvf "${full_file}";; yann@1763: /.git) CT_ExtractGit "${basename}" "${@}";; yann@1763: *) CT_Abort "Don't know how to handle '${basename}${ext}': unknown extension";; yann@63: esac yann@63: yann@1208: # Some tarballs have read-only files... :-( yann@1258: # Because of nochdir, we don't know where we are, so chmod all yann@1258: # the src tree yann@1271: CT_DoExecLog DEBUG chmod -R u+w "${CT_SRC_DIR}" yann@1208: yann@1763: # Don't mark as being extracted for git yann@1763: case "${ext}" in yann@1763: /.git) ;; yann@1763: *) CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${basename}.extracted";; yann@1763: esac yann@1691: CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/.${basename}.extracting" yann@1126: yann@1761: CT_Popd yann@1126: } yann@1126: yann@1763: # Create a working git clone yann@1763: # Usage: CT_ExtractGit [ref] yann@1763: # where 'ref' is the reference to use: yann@1763: # the full name of a branch, like "remotes/origin/branch_name" yann@1763: # a date as understandable by git, like "YYYY-MM-DD[ hh[:mm[:ss]]]" yann@1763: # a tag name yann@1763: CT_ExtractGit() { yann@1763: local basename="${1}" yann@1763: local ref="${2}" yann@1763: local clone_dir yann@1763: local ref_type yann@1763: yann@1763: # pushd now to be able to get git revlist in case ref is a date yann@1763: clone_dir="${CT_TARBALLS_DIR}/${basename}" yann@1763: CT_Pushd "${clone_dir}" yann@1763: yann@1763: # What kind of reference is ${ref} ? yann@1763: if [ -z "${ref}" ]; then yann@1763: # Don't update the clone, keep as-is yann@1763: ref_type=none yann@1763: elif git tag |grep -E "^${ref}$" >/dev/null 2>&1; then yann@1763: ref_type=tag yann@1763: elif git branch -a --no-color |grep -E "^. ${ref}$" >/dev/null 2>&1; then yann@1763: ref_type=branch yann@1763: elif date -d "${ref}" >/dev/null 2>&1; then yann@1763: ref_type=date yann@1763: ref=$(git rev-list -n1 --before="${ref}") yann@1763: else yann@1763: CT_Abort "Reference '${ref}' is an incorrect git reference: neither tag, branch nor date" yann@1763: fi yann@1763: yann@1763: CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/${basename}" yann@1763: CT_DoExecLog ALL ln -sf "${clone_dir}" "${CT_SRC_DIR}/${basename}" yann@1763: yann@1763: case "${ref_type}" in yann@1763: none) ;; yann@1763: *) CT_DoExecLog ALL git checkout "${ref}";; yann@1763: esac yann@1763: yann@1763: CT_Popd yann@1763: } yann@1763: yann@1126: # Patches the specified component yann@1761: # See CT_Extract, above, for explanations on 'nochdir' yann@1761: # Usage: CT_Patch [nochdir] yann@1126: CT_Patch() { yann@1761: local nochdir="$1" yann@1761: local basename yann@1761: local base_file yann@1761: local ver_file yann@1507: local d yann@1507: local -a patch_dirs yann@1507: local bundled_patch_dir yann@1507: local local_patch_dir yann@1126: yann@1761: if [ "${nochdir}" = "nochdir" ]; then yann@1761: shift yann@1761: nochdir="$(pwd)" yann@1761: else yann@1761: nochdir="${CT_SRC_DIR}/${1}" yann@1761: fi yann@1761: yann@1761: basename="$1" yann@1761: base_file="${basename%%-*}" yann@1761: ver_file="${basename#*-}" yann@1761: yann@1126: # Check if already patched yann@1126: if [ -e "${CT_SRC_DIR}/.${basename}.patched" ]; then yann@1126: CT_DoLog DEBUG "Already patched '${basename}'" yann@1126: return 0 yann@63: fi yann@63: yann@1271: # Check if already partially patched yann@1271: if [ -e "${CT_SRC_DIR}/.${basename}.patching" ]; then yann@1271: CT_DoLog ERROR "The '${basename}' sources were partially patched." yann@1271: CT_DoLog ERROR "Please remove first:" yann@1271: CT_DoLog ERROR " - the source dir for '${basename}', in '${CT_SRC_DIR}'" yann@1271: CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${basename}.extracted'" yann@1271: CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${basename}.patching'" yann@1271: CT_Abort "I'll stop now to avoid any carnage..." yann@1271: fi yann@1271: touch "${CT_SRC_DIR}/.${basename}.patching" yann@1271: yann@1761: CT_Pushd "${nochdir}" yann@1123: yann@1126: CT_DoLog EXTRA "Patching '${basename}'" yann@63: yann@1507: bundled_patch_dir="${CT_LIB_DIR}/patches/${base_file}/${ver_file}" fr@1583: local_patch_dir="${CT_LOCAL_PATCH_DIR}/${base_file}/${ver_file}" yann@1507: yann@1507: case "${CT_PATCH_ORDER}" in yann@1507: bundled) patch_dirs=("${bundled_patch_dir}");; yann@1507: local) patch_dirs=("${local_patch_dir}");; yann@1507: bundled,local) patch_dirs=("${bundled_patch_dir}" "${local_patch_dir}");; yann@1508: local,bundled) patch_dirs=("${local_patch_dir}" "${bundled_patch_dir}");; yann@1630: none) patch_dirs=;; yann@1507: esac yann@1507: yann@1507: for d in "${patch_dirs[@]}"; do yann@1507: CT_DoLog DEBUG "Looking for patches in '${d}'..." yann@1507: if [ -n "${d}" -a -d "${d}" ]; then yann@1507: for p in "${d}"/*.patch; do yann@63: if [ -f "${p}" ]; then yann@523: CT_DoLog DEBUG "Applying patch '${p}'" yann@776: CT_DoExecLog ALL patch -g0 -F1 -p1 -f <"${p}" yann@63: fi yann@63: done yann@1509: if [ "${CT_PATCH_SINGLE}" = "y" ]; then yann@1509: break yann@1509: fi yann@63: fi yann@63: done yann@63: yann@508: if [ "${CT_OVERIDE_CONFIG_GUESS_SUB}" = "y" ]; then yann@508: CT_DoLog ALL "Overiding config.guess and config.sub" yann@508: for cfg in config_guess config_sub; do yann@1101: eval ${cfg}="${CT_LIB_DIR}/scripts/${cfg/_/.}" yann@1101: [ -e "${CT_TOP_DIR}/scripts/${cfg/_/.}" ] && eval ${cfg}="${CT_TOP_DIR}/scripts/${cfg/_/.}" yann@776: # Can't use CT_DoExecLog because of the '{} \;' to be passed un-mangled to find yann@508: find . -type f -name "${cfg/_/.}" -exec cp -v "${!cfg}" {} \; |CT_DoLog ALL yann@508: done yann@508: fi yann@508: yann@1718: CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${basename}.patched" yann@1271: CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/.${basename}.patching" yann@1126: yann@1761: CT_Popd yann@63: } yann@63: yann@182: # Two wrappers to call config.(guess|sub) either from CT_TOP_DIR or CT_LIB_DIR. yann@182: # Those from CT_TOP_DIR, if they exist, will be be more recent than those from CT_LIB_DIR. yann@182: CT_DoConfigGuess() { yann@1101: if [ -x "${CT_TOP_DIR}/scripts/config.guess" ]; then yann@1101: "${CT_TOP_DIR}/scripts/config.guess" yann@182: else yann@1101: "${CT_LIB_DIR}/scripts/config.guess" yann@182: fi yann@182: } yann@182: yann@182: CT_DoConfigSub() { yann@1101: if [ -x "${CT_TOP_DIR}/scripts/config.sub" ]; then yann@1101: "${CT_TOP_DIR}/scripts/config.sub" "$@" yann@182: else yann@1101: "${CT_LIB_DIR}/scripts/config.sub" "$@" yann@182: fi yann@182: } yann@182: yann@335: # Compute the target tuple from what is provided by the user yann@335: # Usage: CT_DoBuildTargetTuple yann@63: # In fact this function takes the environment variables to build the target yann@335: # tuple. It is needed both by the normal build sequence, as well as the yann@63: # sample saving sequence. yann@335: CT_DoBuildTargetTuple() { yann@383: # Set the endianness suffix, and the default endianness gcc option yann@63: case "${CT_ARCH_BE},${CT_ARCH_LE}" in yann@383: y,) target_endian_eb=eb yann@383: target_endian_el= yann@391: CT_ARCH_ENDIAN_CFLAG="-mbig-endian" yann@527: CT_ARCH_ENDIAN_LDFLAG="-EB" yann@383: ;; yann@383: ,y) target_endian_eb= yann@383: target_endian_el=el yann@391: CT_ARCH_ENDIAN_CFLAG="-mlittle-endian" yann@527: CT_ARCH_ENDIAN_LDFLAG="-EL" yann@383: ;; yann@63: esac yann@383: yann@965: # Build the default architecture tuple part yann@965: CT_TARGET_ARCH="${CT_ARCH}" yann@965: yann@383: # Set defaults for the system part of the tuple. Can be overriden yann@383: # by architecture-specific values. yann@383: case "${CT_LIBC}" in yann@787: *glibc) CT_TARGET_SYS=gnu;; yann@383: uClibc) CT_TARGET_SYS=uclibc;; yann@1591: *) CT_TARGET_SYS=elf;; yann@63: esac yann@383: yann@391: # Set the default values for ARCH, ABI, CPU, TUNE, FPU and FLOAT yann@497: unset CT_ARCH_ARCH_CFLAG CT_ARCH_ABI_CFLAG CT_ARCH_CPU_CFLAG CT_ARCH_TUNE_CFLAG CT_ARCH_FPU_CFLAG CT_ARCH_FLOAT_CFLAG yann@497: unset CT_ARCH_WITH_ARCH CT_ARCH_WITH_ABI CT_ARCH_WITH_CPU CT_ARCH_WITH_TUNE CT_ARCH_WITH_FPU CT_ARCH_WITH_FLOAT yann@391: [ "${CT_ARCH_ARCH}" ] && { CT_ARCH_ARCH_CFLAG="-march=${CT_ARCH_ARCH}"; CT_ARCH_WITH_ARCH="--with-arch=${CT_ARCH_ARCH}"; } yann@391: [ "${CT_ARCH_ABI}" ] && { CT_ARCH_ABI_CFLAG="-mabi=${CT_ARCH_ABI}"; CT_ARCH_WITH_ABI="--with-abi=${CT_ARCH_ABI}"; } yann@391: [ "${CT_ARCH_CPU}" ] && { CT_ARCH_CPU_CFLAG="-mcpu=${CT_ARCH_CPU}"; CT_ARCH_WITH_CPU="--with-cpu=${CT_ARCH_CPU}"; } yann@405: [ "${CT_ARCH_TUNE}" ] && { CT_ARCH_TUNE_CFLAG="-mtune=${CT_ARCH_TUNE}"; CT_ARCH_WITH_TUNE="--with-tune=${CT_ARCH_TUNE}"; } yann@391: [ "${CT_ARCH_FPU}" ] && { CT_ARCH_FPU_CFLAG="-mfpu=${CT_ARCH_FPU}"; CT_ARCH_WITH_FPU="--with-fpu=${CT_ARCH_FPU}"; } yann@412: [ "${CT_ARCH_FLOAT_SW}" ] && { CT_ARCH_FLOAT_CFLAG="-msoft-float"; CT_ARCH_WITH_FLOAT="--with-float=soft"; } yann@391: yann@965: # Build the default kernel tuple part yann@965: CT_TARGET_KERNEL="${CT_KERNEL}" yann@964: yann@965: # Overide the default values with the components specific settings yann@964: CT_DoArchTupleValues yann@965: CT_DoKernelTupleValues yann@383: yann@391: # Finish the target tuple construction yann@1704: CT_TARGET="${CT_TARGET_ARCH}-" yann@1704: CT_TARGET="${CT_TARGET}${CT_TARGET_VENDOR:+${CT_TARGET_VENDOR}-}" yann@1704: CT_TARGET="${CT_TARGET}${CT_TARGET_KERNEL:+${CT_TARGET_KERNEL}-}" yann@1704: CT_TARGET="${CT_TARGET}${CT_TARGET_SYS}" yann@1094: yann@1094: # Sanity checks yann@1094: __sed_alias="" yann@1094: if [ -n "${CT_TARGET_ALIAS_SED_EXPR}" ]; then yann@1094: __sed_alias=$(echo "${CT_TARGET}" |sed -r -e "${CT_TARGET_ALIAS_SED_EXPR}") yann@1094: fi yann@1094: case ":${CT_TARGET_VENDOR}:${CT_TARGET_ALIAS}:${__sed_alias}:" in yann@1094: :*" "*:*:*:) CT_Abort "Don't use spaces in the vendor string, it breaks things.";; yann@1094: :*"-"*:*:*:) CT_Abort "Don't use dashes in the vendor string, it breaks things.";; yann@1094: :*:*" "*:*:) CT_Abort "Don't use spaces in the target alias, it breaks things.";; yann@1094: :*:*:*" "*:) CT_Abort "Don't use spaces in the target sed transform, it breaks things.";; yann@1094: esac yann@1094: yann@1094: # Canonicalise it yann@1094: CT_TARGET=$(CT_DoConfigSub "${CT_TARGET}") yann@391: # Prepare the target CFLAGS yann@767: CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ENDIAN_CFLAG}" yann@527: CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ARCH_CFLAG}" yann@397: CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ABI_CFLAG}" yann@397: CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_CPU_CFLAG}" yann@397: CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_TUNE_CFLAG}" yann@397: CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FPU_CFLAG}" yann@397: CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FLOAT_CFLAG}" yann@527: yann@527: # Now on for the target LDFLAGS yann@767: CT_ARCH_TARGET_LDFLAGS="${CT_ARCH_TARGET_LDFLAGS} ${CT_ARCH_ENDIAN_LDFLAG}" 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@523: 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@1266: # Log this to the log level required by the user yann@1266: CT_DoLog ${CT_LOG_LEVEL_MAX} "Saving state to restart at step '${state_name}'..." yann@1134: 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@327: y) tar_opt=z; tar_ext=.gz;; yann@327: *) tar_opt=; tar_ext=;; yann@121: esac yann@121: yann@121: CT_DoLog DEBUG " Saving environment and aliases" yann@738: # We must omit shell functions, and some specific bash variables yann@738: # that break when restoring the environment, later. We could do yann@1299: # all the processing in the awk script, but a sed is easier... yann@1299: set |awk ' yann@1017: BEGIN { _p = 1; } yann@1017: $0~/^[^ ]+ \(\)/ { _p = 0; } yann@1017: _p == 1 yann@1017: $0 == "}" { _p = 1; } yann@1017: ' |sed -r -e '/^BASH_(ARGC|ARGV|LINENO|SOURCE|VERSINFO)=/d; yann@738: /^(UID|EUID)=/d; yann@738: /^(FUNCNAME|GROUPS|PPID|SHELLOPTS)=/d;' >"${state_dir}/env.sh" yann@121: yann@1272: CT_DoLog DEBUG " Saving CT_CONFIG_DIR='${CT_CONFIG_DIR}'" yann@1272: CT_Pushd "${CT_CONFIG_DIR}" yann@1272: CT_DoExecLog DEBUG tar cv${tar_opt}f "${state_dir}/config_dir.tar${tar_ext}" . yann@1272: CT_Popd yann@1272: yann@523: 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@776: CT_DoExecLog DEBUG tar cv${tar_opt}f "${state_dir}/cc_core_static_prefix_dir.tar${tar_ext}" . yann@136: CT_Popd yann@136: yann@523: 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@776: CT_DoExecLog DEBUG tar cv${tar_opt}f "${state_dir}/cc_core_shared_prefix_dir.tar${tar_ext}" . yann@121: CT_Popd yann@121: yann@523: CT_DoLog DEBUG " Saving CT_PREFIX_DIR='${CT_PREFIX_DIR}'" yann@121: CT_Pushd "${CT_PREFIX_DIR}" yann@776: CT_DoExecLog DEBUG tar cv${tar_opt}f "${state_dir}/prefix_dir.tar${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@523: 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@1266: yann@1266: # Log this to the log level required by the user yann@1266: CT_DoLog ${CT_LOG_LEVEL_MAX} "Restoring state at step '${state_name}', as requested." yann@121: yann@121: case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in yann@327: y) tar_opt=z; tar_ext=.gz;; yann@327: *) tar_opt=; tar_ext=;; yann@121: esac yann@121: yann@121: CT_DoLog DEBUG " Removing previous build directories" yann@1272: CT_DoForceRmdir "${CT_PREFIX_DIR}" "${CT_CC_CORE_SHARED_PREFIX_DIR}" "${CT_CC_CORE_STATIC_PREFIX_DIR}" "${CT_CONFIG_DIR}" yann@1272: CT_DoExecLog DEBUG mkdir -p "${CT_PREFIX_DIR}" "${CT_CC_CORE_SHARED_PREFIX_DIR}" "${CT_CC_CORE_STATIC_PREFIX_DIR}" "${CT_CONFIG_DIR}" yann@121: yann@523: CT_DoLog DEBUG " Restoring CT_PREFIX_DIR='${CT_PREFIX_DIR}'" yann@121: CT_Pushd "${CT_PREFIX_DIR}" yann@776: CT_DoExecLog DEBUG tar xv${tar_opt}f "${state_dir}/prefix_dir.tar${tar_ext}" yann@121: CT_Popd yann@121: yann@523: 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@776: CT_DoExecLog DEBUG tar xv${tar_opt}f "${state_dir}/cc_core_shared_prefix_dir.tar${tar_ext}" yann@136: CT_Popd yann@136: yann@523: 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@776: CT_DoExecLog DEBUG tar xv${tar_opt}f "${state_dir}/cc_core_static_prefix_dir.tar${tar_ext}" yann@121: CT_Popd yann@121: yann@1272: CT_DoLog DEBUG " Restoring CT_CONFIG_DIR='${CT_CONFIG_DIR}'" yann@1272: CT_Pushd "${CT_CONFIG_DIR}" yann@1272: CT_DoExecLog DEBUG tar xv${tar_opt}f "${state_dir}/config_dir.tar${tar_ext}" yann@1272: CT_Popd yann@1272: 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: }