anthony@2155: # This file contains some usefull common functions -*- sh -*- 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@2388: local ret=$? yann@2388: local intro yann@2388: local file yann@2388: local line yann@2388: local func yann@2388: local step_depth yann@2388: yann@726: # Bail out early in subshell, the upper level shell will act accordingly. yann@726: [ ${BASH_SUBSHELL} -eq 0 ] || exit $ret yann@2388: yann@2388: # Print steps backtrace yann@2388: step_depth=${CT_STEP_COUNT} yann@2388: CT_STEP_COUNT=2 yann@2388: CT_DoLog ERROR "" yann@2388: intro="Build failed" yann@2388: for((step=step_depth; step>1; step--)); do yann@2388: CT_DoLog ERROR ">> ${intro} in step '${CT_STEP_MESSAGE[${step}]}'" yann@2388: intro=" called" yann@2388: done yann@2388: yann@2388: # Print functions backtrace yann@2388: intro="Error happened in" yann@2388: offset=1 yann@2388: CT_DoLog ERROR ">>" yann@2388: for((depth=1; ${BASH_LINENO[$((${depth}-1))]}>0; depth++)); do yann@2388: file="${BASH_SOURCE[${depth}]#${CT_LIB_DIR}/}" yann@2388: case "${depth}" in yann@2388: 1) line="";; yann@2388: *) line="@${BASH_LINENO[${depth}-1]}" yann@2388: esac yann@2388: func="${FUNCNAME[${depth}]}" yann@2388: CT_DoLog ERROR ">> ${intro}: ${func}[${file}${line}]" yann@2388: intro=" called from" yann@2388: done yann@2388: yann@2388: # Help diagnose the error yann@2388: CT_DoLog ERROR ">>" yann@2388: if [ "${CT_LOG_TO_FILE}" = "y" ]; then yann@2388: CT_DoLog ERROR ">> For more info on this error, look at the file: '${tmp_log_file#${CT_TOP_DIR}/}'" yann@2388: fi yann@2388: CT_DoLog ERROR ">> There is a list of known issues, some with workarounds, in:" yann@2388: CT_DoLog ERROR ">> '${CT_DOC_DIR#${CT_TOP_DIR}/}/B - Known issues.txt'" yann@2388: yann@2388: CT_DoLog ERROR "" yann@523: CT_DoLog ERROR "Build failed in step '${CT_STEP_MESSAGE[${CT_STEP_COUNT}]}'" yann@2388: yann@2388: CT_DoLog ERROR "" 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@2339: # - then point stdout to the log file yann@2339: tmp_log_file="${CT_TOP_DIR}/build.log" yann@2339: rm -f "${tmp_log_file}" 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 anthony@2154: CT_LOG_LEVEL_CFG=4 anthony@2154: CT_LOG_LEVEL_FILE=5 anthony@2155: CT_LOG_LEVEL_STATE=6 anthony@2155: CT_LOG_LEVEL_ALL=7 anthony@2155: CT_LOG_LEVEL_DEBUG=8 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 anthony@2154: # - CFG: Output of various "./configure"-type scripts anthony@2154: # - FILE: File / archive unpacking. anthony@2155: # - STATE: State save & restore yann@78: # - ALL: Component's build messages anthony@2155: # - DEBUG: Internal 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@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@2096: printf "%s\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. javier@2382: printf "${CT_LOG_PROGRESS_BAR:+\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@2340: # It is possible to even log local variable assignments (a-la: var=val ./cmd opts) yann@2340: # Usage: CT_DoExecLog [VAR=val...] [parameters...] yann@535: CT_DoExecLog() { yann@535: local level="$1" yann@535: shift yann@2340: ( yann@2340: for i in "$@"; do yann@2340: tmp_log+="'${i}' " yann@2340: done yann@2340: while true; do yann@2340: case "${1}" in yann@2340: *=*) eval export "'${1}'"; shift;; yann@2340: *) break;; yann@2340: esac yann@2340: done yann@2340: CT_DoLog DEBUG "==> Executing: ${tmp_log}" yann@668: "${@}" 2>&1 |CT_DoLog "${level}" yann@2340: ) yann@2340: # Catch failure of the sub-shell yann@2340: [ $? -eq 0 ] 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@2051: # Remove entries referring to . and other relative paths js@2044: # Usage: CT_SanitizePath js@2044: CT_SanitizePath() { js@2044: local new yann@2051: local p js@2044: local IFS=: js@2044: for p in $PATH; do yann@2051: # Only accept absolute paths; yann@2051: # Note: as a special case the empty string in PATH is equivalent to . yann@2051: if [ -n "${p}" -a -z "${p%%/*}" ]; then js@2044: new="${new}${new:+:}${p}" js@2044: fi js@2044: done js@2044: PATH="${new}" js@2044: } js@2044: yann@2280: # Sanitise the directory name contained in the variable passed as argument: yann@2280: # - remove duplicate / yann@2280: # Usage: CT_SanitiseVarDir CT_PREFIX_DIR yann@2280: CT_SanitiseVarDir() { yann@2280: local var yann@2280: local old_dir yann@2280: local new_dir yann@2280: yann@2280: for var in "$@"; do yann@2280: eval "old_dir=\"\${${var}}\"" yann@2280: new_dir="$( printf "${old_dir}" \ yann@2280: |sed -r -e 's:/+:/:g;' \ yann@2280: )" yann@2280: eval "${var}=\"${new_dir}\"" yann@2280: CT_DoLog DEBUG "Sanitised '${var}': '${old_dir}' -> '${new_dir}'" yann@2280: done yann@2280: } yann@2280: 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@1909: CT_DoLog DEBUG "Testing '! ( $* )'" 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@1909: CT_DoLog DEBUG "Testing '! ( $* )'" 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@1909: CT_DoLog DEBUG "Testing '$*'" 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@2383: 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 titus@1956: case "$CT_SYS_OS" in yann@2029: Linux|CYGWIN*) titus@1956: mode="$(stat -c '%a' "$(dirname "${dir}")")" titus@1956: ;; titus@1956: Darwin|*BSD) titus@1956: mode="$(stat -f '%Lp' "$(dirname "${dir}")")" titus@1956: ;; titus@1956: *) titus@1956: CT_Abort "Unhandled host OS $CT_SYS_OS" titus@1956: ;; titus@1956: esac 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@2608: # Build up the list of allowed tarball extensions yann@2608: # Add them in the prefered order; most preferred comes first yann@2608: CT_DoListTarballExt() { yann@2609: if [ "${CT_CONFIGURE_has_xzutils}" = "y" ]; then yann@2609: printf ".tar.xz\n" yann@2609: fi yann@2608: printf ".tar.bz2\n" yann@2608: printf ".tar.gz\n.tgz\n" yann@2608: printf ".tar\n" yann@2608: } yann@2608: 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@2608: for ext in ${first_ext} $(CT_DoListTarballExt) /.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@2204: # Try to retrieve the specified URL (HTTP or FTP) yann@2204: # Usage: CT_DoGetFile yann@2204: # This functions always returns true (0), as it can be legitimate not yann@2204: # to find the requested URL (think about snapshots, different layouts yann@2204: # for different gcc versions, etc...). yann@2204: CT_DoGetFile() { yann@2501: local url="${1}" yann@2501: local dest="${CT_TARBALLS_DIR}/${url##*/}" yann@2205: local tmp="${dest}.tmp-dl" yann@2204: # OK, just look if we have them... yann@2204: # We are sure at least one is available, ./configure checked for it. yann@2204: local _curl=$(CT_Which curl) yann@2204: local _wget=$(CT_Which wget) yann@2204: _curl="${_curl:-false}" yann@2204: _wget="${_wget:-false}" yann@2204: yann@2205: # Remove potential left-over from a previous run yann@2205: rm -f "${tmp}" yann@2205: yann@63: # Some (very old!) FTP server might not support the passive mode, thus yann@2204: # retry without. yann@2204: # We also retry a few times, in case there is a transient error (eg. behind yann@2204: # a dynamic IP that changes during the transfer...) yann@2204: # With automated download as we are doing, it can be very dangerous to yann@2204: # continue the downloads. It's far better to simply overwrite the yann@2204: # 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@2204: # For curl, no good progress indicator is available. So, be silent. yann@2501: if CT_DoExecLog ALL "${_curl}" --ftp-pasv --retry 3 --connect-timeout ${CT_CONNECT_TIMEOUT} -L -f -s -o "${tmp}" "${url}" \ yann@2501: || CT_DoExecLog ALL "${_curl}" --retry 3 --connect-timeout ${CT_CONNECT_TIMEOUT} -L -f -s -o "${tmp}" "${url}" \ yann@2501: || CT_DoExecLog ALL "${_wget}" --passive-ftp --tries=3 -T ${CT_CONNECT_TIMEOUT} -nc --progress=dot:binary -O "${tmp}" "${url}" \ yann@2501: || CT_DoExecLog ALL "${_wget}" --tries=3 -T ${CT_CONNECT_TIMEOUT} -nc --progress=dot:binary -O "${tmp}" "${url}" \ yann@2205: ; then yann@2205: # One of them succeeded, good! yann@2205: mv "${tmp}" "${dest}" yann@2205: else yann@2205: # Woops... yann@2205: rm -f "${tmp}" yann@2205: 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@2608: for ext in ${first_ext} $(CT_DoListTarballExt) ''; 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@2594: local -a URLS yann@2594: local url 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@2599: if CT_GetLocal "${file}" ${first_ext}; then yann@2599: return 0 yann@2599: fi yann@1113: # No, it does not... yann@63: yann@754: # Try to retrieve the file yann@788: CT_DoLog EXTRA "Retrieving '${file}'" yann@754: yann@1022: # Add URLs on the LAN mirror yann@1022: if [ "${CT_USE_MIRROR}" = "y" ]; then yann@1294: CT_TestOrAbort "Please set the mirror base URL" -n "${CT_MIRROR_BASE_URL}" yann@2594: URLS+=( "${CT_MIRROR_BASE_URL}/${file%-*}" ) yann@2594: URLS+=( "${CT_MIRROR_BASE_URL}" ) yann@2593: fi yann@695: yann@2595: if [ "${CT_FORBID_DOWNLOAD}" != "y" ]; then yann@2595: URLS+=( "${@}" ) yann@2595: 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@2608: for ext in ${first_ext} $(CT_DoListTarballExt) ''; do yann@102: # Try all urls in turn yann@2594: for url in "${URLS[@]}"; do yann@2589: [ -n "${url}" ] || continue yann@523: CT_DoLog DEBUG "Trying '${url}/${file}${ext}'" yann@265: CT_DoGetFile "${url}/${file}${ext}" yann@2492: if [ -f "${CT_TARBALLS_DIR}/${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: yann@2599: # Just return error, someone may want to catch and handle the error yann@2492: # (eg. glibc/eglibc add-ons can be missing). yann@2492: return 1 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@2592: # First try locally, then the mirror yann@2592: if CT_GetFile "${basename}"; then yann@2592: # Got it! Return early! :-) yann@2592: return 0 yann@2592: fi yann@1113: yann@2595: if [ "${CT_FORBID_DOWNLOAD}" = "y" ]; then yann@2595: CT_DoLog WARN "Downloads forbidden, not trying cvs retrieval" yann@2595: return 1 yann@2595: fi yann@2595: 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@2590: # First try locally, then the mirror yann@2590: if CT_GetFile "${basename}"; then yann@2590: # Got it! Return early! :-) yann@2590: return 0 yann@2590: fi yann@1244: yann@2595: if [ "${CT_FORBID_DOWNLOAD}" = "y" ]; then yann@2595: CT_DoLog WARN "Downloads forbidden, not trying svn retrieval" yann@2595: return 1 yann@2595: fi yann@2595: yann@1244: CT_MktempDir tmp_dir yann@1244: CT_Pushd "${tmp_dir}" yann@1244: yann@2494: if ! CT_DoExecLog ALL svn export ${rev:+-r ${rev}} "${uri}" "${basename}"; then yann@2494: CT_DoLog WARN "Could not retrieve '${basename}'" yann@2494: return 1 yann@2494: fi 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@2595: if [ "${CT_FORBID_DOWNLOAD}" = "y" ]; then yann@2595: CT_DoLog WARN "Downloads forbidden, not trying git retrieval" yann@2595: return 1 yann@2595: fi yann@2595: 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@2605: local -a tar_opts 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@2493: CT_DoLog WARN "'${basename}' not found in '${CT_TARBALLS_DIR}'" yann@2493: return 1 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}'" benoit@2574: CT_DoExecLog FILE mkdir -p "${basename}" yann@2605: tar_opts=( "--strip-components=1" ) yann@2605: tar_opts+=( "-C" "${basename}" ) yann@2605: tar_opts+=( "-xv" ) yann@63: case "${ext}" in yann@2609: .tar.xz) CT_DoExecLog FILE tar "${tar_opts[@]}" --use-compress-program=xz -f "${full_file}";; yann@2608: .tar.bz2) CT_DoExecLog FILE tar "${tar_opts[@]}" -j -f "${full_file}";; yann@2608: .tar.gz|.tgz) CT_DoExecLog FILE tar "${tar_opts[@]}" -z -f "${full_file}";; yann@2608: .tar) CT_DoExecLog FILE tar "${tar_opts[@]}" -f "${full_file}";; yann@1763: /.git) CT_ExtractGit "${basename}" "${@}";; yann@2493: *) CT_DoLog WARN "Don't know how to handle '${basename}${ext}': unknown extension" yann@2493: return 1 yann@2493: ;; yann@63: esac yann@63: 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) ;; anthony@2154: *) CT_DoExecLog FILE 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@1901: # Usage: CT_Patch [nochdir] yann@1901: # If the package directory is *not* packagename-packageversion, then yann@1901: # the caller must cd into the proper directory first, and call us yann@1901: # with nochdir yann@1126: CT_Patch() { yann@1761: local nochdir="$1" yann@1901: local pkgname yann@1901: local version yann@1901: local pkgdir 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@1906: pkgname="$1" yann@1906: version="$2" yann@1906: pkgdir="${pkgname}-${version}" yann@1761: nochdir="$(pwd)" yann@1761: else yann@1906: pkgname="$1" yann@1906: version="$2" yann@1906: pkgdir="${pkgname}-${version}" yann@1901: nochdir="${CT_SRC_DIR}/${pkgdir}" yann@1761: fi yann@1761: yann@1126: # Check if already patched yann@1901: if [ -e "${CT_SRC_DIR}/.${pkgdir}.patched" ]; then yann@1901: CT_DoLog DEBUG "Already patched '${pkgdir}'" yann@1126: return 0 yann@63: fi yann@63: yann@1271: # Check if already partially patched yann@1901: if [ -e "${CT_SRC_DIR}/.${pkgdir}.patching" ]; then yann@1901: CT_DoLog ERROR "The '${pkgdir}' sources were partially patched." yann@1271: CT_DoLog ERROR "Please remove first:" yann@1901: CT_DoLog ERROR " - the source dir for '${pkgdir}', in '${CT_SRC_DIR}'" yann@1901: CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${pkgdir}.extracted'" yann@1901: CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${pkgdir}.patching'" yann@1271: CT_Abort "I'll stop now to avoid any carnage..." yann@1271: fi yann@1901: touch "${CT_SRC_DIR}/.${pkgdir}.patching" yann@1271: yann@1761: CT_Pushd "${nochdir}" yann@1123: yann@1901: CT_DoLog EXTRA "Patching '${pkgdir}'" yann@63: yann@1901: bundled_patch_dir="${CT_LIB_DIR}/patches/${pkgname}/${version}" yann@1901: local_patch_dir="${CT_LOCAL_PATCH_DIR}/${pkgname}/${version}" 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@1765: CT_DoExecLog ALL patch --no-backup-if-mismatch -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@1903: CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${pkgdir}.patched" yann@1903: CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/.${pkgdir}.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 bartvdrmeulen@1896: CT_TARGET="${CT_TARGET_ARCH}" bartvdrmeulen@1896: CT_TARGET="${CT_TARGET}${CT_TARGET_VENDOR:+-${CT_TARGET_VENDOR}}" bartvdrmeulen@1896: CT_TARGET="${CT_TARGET}${CT_TARGET_KERNEL:+-${CT_TARGET_KERNEL}}" bartvdrmeulen@1896: CT_TARGET="${CT_TARGET}${CT_TARGET_SYS:+-${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@1907: # This function creates a tarball of the specified directory, but yann@1907: # only if it exists yann@1907: # Usage: CT_DoTarballIfExists [extra_tar_options [...]] yann@1907: CT_DoTarballIfExists() { yann@1907: local dir="$1" yann@1907: local tarball="$2" yann@1907: shift 2 yann@1907: local -a extra_tar_opts=( "$@" ) yann@1908: local -a compress yann@1907: yann@1907: case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in yann@1908: y) compress=( gzip -c -3 - ); tar_ext=.gz;; yann@1908: *) compress=( cat - ); tar_ext=;; yann@1907: esac yann@1907: yann@1907: if [ -d "${dir}" ]; then yann@1907: CT_DoLog DEBUG " Saving '${dir}'" yann@1908: { tar c -C "${dir}" -v -f - "${extra_tar_opts[@]}" . \ yann@1908: |"${compress[@]}" >"${tarball}.tar${tar_ext}" ; anthony@2155: } 2>&1 |sed -r -e 's/^/ /;' |CT_DoLog STATE yann@1907: else anthony@2155: CT_DoLog STATE " Not saving '${dir}': does not exist" yann@1907: fi yann@1907: } yann@1907: yann@1907: # This function extracts a tarball to the specified directory, but yann@1907: # only if the tarball exists anthony@2155: # Usage: CT_DoExtractTarballIfExists [extra_tar_options [...]] yann@1907: CT_DoExtractTarballIfExists() { yann@1907: local tarball="$1" yann@1907: local dir="$2" yann@1907: shift 2 yann@1907: local -a extra_tar_opts=( "$@" ) yann@1908: local -a uncompress yann@1907: yann@1907: case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in yann@1908: y) uncompress=( gzip -c -d ); tar_ext=.gz;; yann@1908: *) uncompress=( cat ); tar_ext=;; yann@1907: esac yann@1907: yann@1907: if [ -f "${tarball}.tar${tar_ext}" ]; then yann@1907: CT_DoLog DEBUG " Restoring '${dir}'" yann@1907: CT_DoForceRmdir "${dir}" yann@1907: CT_DoExecLog DEBUG mkdir -p "${dir}" yann@1908: { "${uncompress[@]}" "${tarball}.tar${tar_ext}" \ yann@1908: |tar x -C "${dir}" -v -f - "${extra_tar_opts[@]}" ; anthony@2155: } 2>&1 |sed -r -e 's/^/ /;' |CT_DoLog STATE yann@1907: else anthony@2155: CT_DoLog STATE " Not restoring '${dir}': does not exist" yann@1907: fi yann@1907: } yann@1907: 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: anthony@2155: CT_DoLog STATE " 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@2308: CT_DoTarballIfExists "${CT_BUILDTOOLS_PREFIX_DIR}" "${state_dir}/buildtools_dir" yann@2502: CT_DoTarballIfExists "${CT_COMPLIBS_DIR}" "${state_dir}/complibs_dir" yann@1907: CT_DoTarballIfExists "${CT_CONFIG_DIR}" "${state_dir}/config_dir" yann@1907: CT_DoTarballIfExists "${CT_CC_CORE_STATIC_PREFIX_DIR}" "${state_dir}/cc_core_static_prefix_dir" yann@1907: CT_DoTarballIfExists "${CT_CC_CORE_SHARED_PREFIX_DIR}" "${state_dir}/cc_core_shared_prefix_dir" yann@1907: CT_DoTarballIfExists "${CT_PREFIX_DIR}" "${state_dir}/prefix_dir" --exclude '*.log' yann@121: yann@2339: CT_DoLog STATE " Saving log file" yann@2339: exec >/dev/null yann@2339: case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in yann@2339: y) gzip -3 -c "${tmp_log_file}" >"${state_dir}/log.gz";; yann@2339: *) cat "${tmp_log_file}" >"${state_dir}/log";; yann@2339: esac yann@2339: exec >>"${tmp_log_file}" 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@1907: CT_DoExtractTarballIfExists "${state_dir}/prefix_dir" "${CT_PREFIX_DIR}" yann@1907: CT_DoExtractTarballIfExists "${state_dir}/cc_core_shared_prefix_dir" "${CT_CC_CORE_SHARED_PREFIX_DIR}" yann@1907: CT_DoExtractTarballIfExists "${state_dir}/cc_core_static_prefix_dir" "${CT_CC_CORE_STATIC_PREFIX_DIR}" yann@1907: CT_DoExtractTarballIfExists "${state_dir}/config_dir" "${CT_CONFIG_DIR}" yann@2502: CT_DoExtractTarballIfExists "${state_dir}/complibs_dir" "${CT_COMPLIBS_DIR}" yann@2308: CT_DoExtractTarballIfExists "${state_dir}/buildtools_dir" "${CT_BUILDTOOLS_PREFIX_DIR}" yann@1894: yann@121: # Restore the environment, discarding any error message yann@121: # (for example, read-only bash internals) anthony@2155: CT_DoLog STATE " 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@2339: CT_DoLog STATE " Restoring log file" yann@2339: exec >/dev/null yann@2339: case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in yann@2339: y) zcat "${state_dir}/log.gz" >"${tmp_log_file}";; yann@2339: *) cat "${state_dir}/log" >"${tmp_log_file}";; yann@2339: esac yann@2339: cat "${state_dir}/tail.log" >>"${tmp_log_file}" yann@2339: exec >>"${tmp_log_file}" yann@2339: rm -f "${state_dir}/tail.log" yann@121: }