scripts/functions
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Sun Mar 20 00:02:21 2011 +0100 (2011-03-20)
changeset 2339 730e2d63296b
parent 2331 bbfd61c12916
child 2340 580f427595bc
permissions -rw-r--r--
scripts: leave changelog in build dir, copy to install dir

Users tend to look for the build log in the current working directory,
rather than in the toolchain's installation dir. While bundling the build
log in the toolchain installation dir is nice for distribution and review,
it can be easier to have the build log readily available in the working
directory, as it is quicker to get to it.

So, the build log stays in the working directory until the toolchain is
completely and successfully built, and then a (compressed) copy is made.

Reported-by: Trevor Woerner <twoerner@gmail.com>
Signed-off-by: "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
     1 # This file contains some usefull common functions -*- sh -*-
     2 # Copyright 2007 Yann E. MORIN
     3 # Licensed under the GPL v2. See COPYING in the root of this package
     4 
     5 # Prepare the fault handler
     6 CT_OnError() {
     7     ret=$?
     8     # Bail out early in subshell, the upper level shell will act accordingly.
     9     [ ${BASH_SUBSHELL} -eq 0 ] || exit $ret
    10     CT_DoLog ERROR "Build failed in step '${CT_STEP_MESSAGE[${CT_STEP_COUNT}]}'"
    11     for((step=(CT_STEP_COUNT-1); step>1; step--)); do
    12         CT_DoLog ERROR "      called in step '${CT_STEP_MESSAGE[${step}]}'"
    13     done
    14     CT_DoLog ERROR "Error happened in '${BASH_SOURCE[1]}' in function '${FUNCNAME[1]}' (line unknown, sorry)"
    15     for((depth=2; ${BASH_LINENO[$((${depth}-1))]}>0; depth++)); do
    16         CT_DoLog ERROR "      called from '${BASH_SOURCE[${depth}]}' at line # ${BASH_LINENO[${depth}-1]} in function '${FUNCNAME[${depth}]}'"
    17     done
    18     [ "${CT_LOG_TO_FILE}" = "y" ] && CT_DoLog ERROR "Look at '${tmp_log_file}' for more info on this error."
    19     CT_STEP_COUNT=1
    20     CT_DoEnd ERROR
    21     exit $ret
    22 }
    23 
    24 # Install the fault handler
    25 trap CT_OnError ERR
    26 
    27 # Inherit the fault handler in subshells and functions
    28 set -E
    29 
    30 # Make pipes fail on the _first_ failed command
    31 # Not supported on bash < 3.x, but we need it, so drop the obsoleting bash-2.x
    32 set -o pipefail
    33 
    34 # Don't hash commands' locations, and search every time it is requested.
    35 # This is slow, but needed because of the static/shared core gcc which shall
    36 # always match to shared if it exists, and only fallback to static if the
    37 # shared is not found
    38 set +o hashall
    39 
    40 # Log policy:
    41 #  - first of all, save stdout so we can see the live logs: fd #6
    42 exec 6>&1
    43 #  - then point stdout to the log file
    44 tmp_log_file="${CT_TOP_DIR}/build.log"
    45 rm -f "${tmp_log_file}"
    46 exec >>"${tmp_log_file}"
    47 
    48 # The different log levels:
    49 CT_LOG_LEVEL_ERROR=0
    50 CT_LOG_LEVEL_WARN=1
    51 CT_LOG_LEVEL_INFO=2
    52 CT_LOG_LEVEL_EXTRA=3
    53 CT_LOG_LEVEL_CFG=4
    54 CT_LOG_LEVEL_FILE=5
    55 CT_LOG_LEVEL_STATE=6
    56 CT_LOG_LEVEL_ALL=7
    57 CT_LOG_LEVEL_DEBUG=8
    58 
    59 # Make it easy to use \n and !
    60 CR=$(printf "\n")
    61 BANG='!'
    62 
    63 # A function to log what is happening
    64 # Different log level are available:
    65 #   - ERROR:   A serious, fatal error occurred
    66 #   - WARN:    A non fatal, non serious error occurred, take your responsbility with the generated build
    67 #   - INFO:    Informational messages
    68 #   - EXTRA:   Extra informational messages
    69 #   - CFG:     Output of various "./configure"-type scripts
    70 #   - FILE:    File / archive unpacking.
    71 #   - STATE:   State save & restore
    72 #   - ALL:     Component's build messages
    73 #   - DEBUG:   Internal debug messages
    74 # Usage: CT_DoLog <level> [message]
    75 # If message is empty, then stdin will be logged.
    76 CT_DoLog() {
    77     local max_level LEVEL level cur_l cur_L
    78     local l
    79     eval max_level="\${CT_LOG_LEVEL_${CT_LOG_LEVEL_MAX}}"
    80     # Set the maximum log level to DEBUG if we have none
    81     [ -z "${max_level}" ] && max_level=${CT_LOG_LEVEL_DEBUG}
    82 
    83     LEVEL="$1"; shift
    84     eval level="\${CT_LOG_LEVEL_${LEVEL}}"
    85 
    86     if [ $# -eq 0 ]; then
    87         cat -
    88     else
    89         printf "%s\n" "${*}"
    90     fi |( IFS="${CR}" # We want the full lines, even leading spaces
    91           _prog_bar_cpt=0
    92           _prog_bar[0]='/'
    93           _prog_bar[1]='-'
    94           _prog_bar[2]='\'
    95           _prog_bar[3]='|'
    96           indent=$((2*CT_STEP_COUNT))
    97           while read line; do
    98               case "${CT_LOG_SEE_TOOLS_WARN},${line}" in
    99                 y,*"warning:"*)         cur_L=WARN; cur_l=${CT_LOG_LEVEL_WARN};;
   100                 y,*"WARNING:"*)         cur_L=WARN; cur_l=${CT_LOG_LEVEL_WARN};;
   101                 *"error:"*)             cur_L=ERROR; cur_l=${CT_LOG_LEVEL_ERROR};;
   102                 *"make["*"]: *** ["*)   cur_L=ERROR; cur_l=${CT_LOG_LEVEL_ERROR};;
   103                 *)                      cur_L="${LEVEL}"; cur_l="${level}";;
   104               esac
   105               # There will always be a log file (stdout, fd #1), be it /dev/null
   106               printf "[%-5s]%*s%s%s\n" "${cur_L}" "${indent}" " " "${line}"
   107               if [ ${cur_l} -le ${max_level} ]; then
   108                   # Only print to console (fd #6) if log level is high enough.
   109                   printf "\r[%-5s]%*s%s%s\n" "${cur_L}" "${indent}" " " "${line}" >&6
   110               fi
   111               if [ "${CT_LOG_PROGRESS_BAR}" = "y" ]; then
   112                   printf "\r[%02d:%02d] %s " $((SECONDS/60)) $((SECONDS%60)) "${_prog_bar[$((_prog_bar_cpt/10))]}" >&6
   113                   _prog_bar_cpt=$(((_prog_bar_cpt+1)%40))
   114               fi
   115           done
   116         )
   117 
   118     return 0
   119 }
   120 
   121 # Execute an action, and log its messages
   122 # Usage: [VAR=val...] CT_DoExecLog <level> <command [parameters...]>
   123 CT_DoExecLog() {
   124     local level="$1"
   125     shift
   126     CT_DoLog DEBUG "==> Executing: '${*}'"
   127     "${@}" 2>&1 |CT_DoLog "${level}"
   128 }
   129 
   130 # Tail message to be logged whatever happens
   131 # Usage: CT_DoEnd <level>
   132 CT_DoEnd()
   133 {
   134     local level="$1"
   135     CT_STOP_DATE=$(CT_DoDate +%s%N)
   136     CT_STOP_DATE_HUMAN=$(CT_DoDate +%Y%m%d.%H%M%S)
   137     if [ "${level}" != "ERROR" ]; then
   138         CT_DoLog "${level:-INFO}" "Build completed at ${CT_STOP_DATE_HUMAN}"
   139     fi
   140     elapsed=$((CT_STOP_DATE-CT_STAR_DATE))
   141     elapsed_min=$((elapsed/(60*1000*1000*1000)))
   142     elapsed_sec=$(printf "%02d" $(((elapsed%(60*1000*1000*1000))/(1000*1000*1000))))
   143     elapsed_csec=$(printf "%02d" $(((elapsed%(1000*1000*1000))/(10*1000*1000))))
   144     CT_DoLog ${level:-INFO} "(elapsed: ${elapsed_min}:${elapsed_sec}.${elapsed_csec})"
   145 }
   146 
   147 # Remove entries referring to . and other relative paths
   148 # Usage: CT_SanitizePath
   149 CT_SanitizePath() {
   150     local new
   151     local p
   152     local IFS=:
   153     for p in $PATH; do
   154         # Only accept absolute paths;
   155         # Note: as a special case the empty string in PATH is equivalent to .
   156         if [ -n "${p}" -a -z "${p%%/*}" ]; then
   157             new="${new}${new:+:}${p}"
   158         fi
   159     done
   160     PATH="${new}"
   161 }
   162 
   163 # Sanitise the directory name contained in the variable passed as argument:
   164 # - remove duplicate /
   165 # Usage: CT_SanitiseVarDir CT_PREFIX_DIR
   166 CT_SanitiseVarDir() {
   167     local var
   168     local old_dir
   169     local new_dir
   170 
   171     for var in "$@"; do
   172         eval "old_dir=\"\${${var}}\""
   173         new_dir="$( printf "${old_dir}"     \
   174                     |sed -r -e 's:/+:/:g;'  \
   175                   )"
   176         eval "${var}=\"${new_dir}\""
   177         CT_DoLog DEBUG "Sanitised '${var}': '${old_dir}' -> '${new_dir}'"
   178     done
   179 }
   180 
   181 # Abort the execution with an error message
   182 # Usage: CT_Abort <message>
   183 CT_Abort() {
   184     CT_DoLog ERROR "$1"
   185     exit 1
   186 }
   187 
   188 # Test a condition, and print a message if satisfied
   189 # Usage: CT_Test <message> <tests>
   190 CT_Test() {
   191     local ret
   192     local m="$1"
   193     shift
   194     CT_DoLog DEBUG "Testing '! ( $* )'"
   195     test "$@" && CT_DoLog WARN "$m"
   196     return 0
   197 }
   198 
   199 # Test a condition, and abort with an error message if satisfied
   200 # Usage: CT_TestAndAbort <message> <tests>
   201 CT_TestAndAbort() {
   202     local m="$1"
   203     shift
   204     CT_DoLog DEBUG "Testing '! ( $* )'"
   205     test "$@" && CT_Abort "$m"
   206     return 0
   207 }
   208 
   209 # Test a condition, and abort with an error message if not satisfied
   210 # Usage: CT_TestAndAbort <message> <tests>
   211 CT_TestOrAbort() {
   212     local m="$1"
   213     shift
   214     CT_DoLog DEBUG "Testing '$*'"
   215     test "$@" || CT_Abort "$m"
   216     return 0
   217 }
   218 
   219 # Test the presence of a tool, or abort if not found
   220 # Usage: CT_HasOrAbort <tool>
   221 CT_HasOrAbort() {
   222     CT_TestAndAbort "'${1}' not found and needed for successful toolchain build." -z "$(CT_Which "${1}")"
   223     return 0
   224 }
   225 
   226 # Search a program: wrap "which" for those system where
   227 # "which" verbosely says there is no match (Mandriva is
   228 # such a sucker...)
   229 # Usage: CT_Which <filename>
   230 CT_Which() {
   231   which "$1" 2>/dev/null || true
   232 }
   233 
   234 # Get current date with nanosecond precision
   235 # On those system not supporting nanosecond precision, faked with rounding down
   236 # to the highest entire second
   237 # Usage: CT_DoDate <fmt>
   238 CT_DoDate() {
   239     date "$1" |sed -r -e 's/N$/000000000/;'
   240 }
   241 
   242 CT_STEP_COUNT=1
   243 CT_STEP_MESSAGE[${CT_STEP_COUNT}]="<none>"
   244 # Memorise a step being done so that any error is caught
   245 # Usage: CT_DoStep <loglevel> <message>
   246 CT_DoStep() {
   247     local start=$(CT_DoDate +%s%N)
   248     CT_DoLog "$1" "================================================================="
   249     CT_DoLog "$1" "$2"
   250     CT_STEP_COUNT=$((CT_STEP_COUNT+1))
   251     CT_STEP_LEVEL[${CT_STEP_COUNT}]="$1"; shift
   252     CT_STEP_START[${CT_STEP_COUNT}]="${start}"
   253     CT_STEP_MESSAGE[${CT_STEP_COUNT}]="$1"
   254     return 0
   255 }
   256 
   257 # End the step just being done
   258 # Usage: CT_EndStep
   259 CT_EndStep() {
   260     local stop=$(CT_DoDate +%s%N)
   261     local duration=$(printf "%032d" $((stop-${CT_STEP_START[${CT_STEP_COUNT}]})) |sed -r -e 's/([[:digit:]]{2})[[:digit:]]{7}$/\.\1/; s/^0+//; s/^\./0\./;')
   262     local elapsed=$(printf "%02d:%02d" $((SECONDS/60)) $((SECONDS%60)))
   263     local level="${CT_STEP_LEVEL[${CT_STEP_COUNT}]}"
   264     local message="${CT_STEP_MESSAGE[${CT_STEP_COUNT}]}"
   265     CT_STEP_COUNT=$((CT_STEP_COUNT-1))
   266     CT_DoLog "${level}" "${message}: done in ${duration}s (at ${elapsed})"
   267     return 0
   268 }
   269 
   270 # Pushes into a directory, and pops back
   271 CT_Pushd() {
   272     pushd "$1" >/dev/null 2>&1
   273 }
   274 CT_Popd() {
   275     popd >/dev/null 2>&1
   276 }
   277 
   278 # Creates a temporary directory
   279 # $1: variable to assign to
   280 # Usage: CT_MktempDir foo
   281 CT_MktempDir() {
   282     # Some mktemp do not allow more than 6 Xs
   283     eval "$1"=$(mktemp -q -d "${CT_BUILD_DIR}/tmp.XXXXXX")
   284     CT_TestOrAbort "Could not make temporary directory" -n "${!1}" -a -d "${!1}"
   285     CT_DoLog DEBUG "Made temporary directory '${!1}'"
   286     return 0
   287 }
   288 
   289 # Removes one or more directories, even if it is read-only, or its parent is
   290 # Usage: CT_DoForceRmdir dir [...]
   291 CT_DoForceRmdir() {
   292     local dir
   293     local mode
   294     for dir in "${@}"; do
   295         [ -d "${dir}" ] || continue
   296         case "$CT_SYS_OS" in
   297             Linux|CYGWIN*)
   298                 mode="$(stat -c '%a' "$(dirname "${dir}")")"
   299                 ;;
   300             Darwin|*BSD)
   301                 mode="$(stat -f '%Lp' "$(dirname "${dir}")")"
   302                 ;;
   303             *)
   304                 CT_Abort "Unhandled host OS $CT_SYS_OS"
   305                 ;;
   306         esac
   307         CT_DoExecLog ALL chmod u+w "$(dirname "${dir}")"
   308         CT_DoExecLog ALL chmod -R u+w "${dir}"
   309         CT_DoExecLog ALL rm -rf "${dir}"
   310         CT_DoExecLog ALL chmod ${mode} "$(dirname "${dir}")"
   311     done
   312 }
   313 
   314 # Echoes the specified string on stdout until the pipe breaks.
   315 # Doesn't fail
   316 # $1: string to echo
   317 # Usage: CT_DoYes "" |make oldconfig
   318 CT_DoYes() {
   319     yes "$1" || true
   320 }
   321 
   322 # Add the specified directory to LD_LIBRARY_PATH, and export it
   323 # If the specified patch is already present, just export
   324 # $1: path to add
   325 # $2: add as 'first' or 'last' path, 'first' is assumed if $2 is empty
   326 # Usage CT_SetLibPath /some/where/lib [first|last]
   327 CT_SetLibPath() {
   328     local path="$1"
   329     local pos="$2"
   330 
   331     case ":${LD_LIBRARY_PATH}:" in
   332         *:"${path}":*)  ;;
   333         *)  case "${pos}" in
   334                 last)
   335                     CT_DoLog DEBUG "Adding '${path}' at end of LD_LIBRARY_PATH"
   336                     LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}${path}"
   337                     ;;
   338                 first|"")
   339                     CT_DoLog DEBUG "Adding '${path}' at start of LD_LIBRARY_PATH"
   340                     LD_LIBRARY_PATH="${path}${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
   341                     ;;
   342                 *)
   343                     CT_Abort "Incorrect position '${pos}' to add '${path}' to LD_LIBRARY_PATH"
   344                     ;;
   345             esac
   346             ;;
   347     esac
   348     CT_DoLog DEBUG "==> LD_LIBRARY_PATH='${LD_LIBRARY_PATH}'"
   349     export LD_LIBRARY_PATH
   350 }
   351 
   352 # Get the file name extension of a component
   353 # Usage: CT_GetFileExtension <component_name-component_version> [extension]
   354 # If found, echoes the extension to stdout, and return 0
   355 # If not found, echoes nothing on stdout, and return !0.
   356 CT_GetFileExtension() {
   357     local ext
   358     local file="$1"
   359     shift
   360     local first_ext="$1"
   361 
   362     # we need to also check for an empty extension for those very
   363     # peculiar components that don't have one (such as sstrip from
   364     # buildroot).
   365     for ext in ${first_ext} .tar.gz .tar.bz2 .tgz .tar /.git ''; do
   366         if [ -e "${CT_TARBALLS_DIR}/${file}${ext}" ]; then
   367             echo "${ext}"
   368             exit 0
   369         fi
   370     done
   371 
   372     exit 1
   373 }
   374 
   375 # Try to retrieve the specified URL (HTTP or FTP)
   376 # Usage: CT_DoGetFile <URL>
   377 # This functions always returns true (0), as it can be legitimate not
   378 # to find the requested URL (think about snapshots, different layouts
   379 # for different gcc versions, etc...).
   380 CT_DoGetFile() {
   381     local dest="${1##*/}"
   382     local tmp="${dest}.tmp-dl"
   383     # OK, just look if we have them...
   384     # We are sure at least one is available, ./configure checked for it.
   385     local _curl=$(CT_Which curl)
   386     local _wget=$(CT_Which wget)
   387     _curl="${_curl:-false}"
   388     _wget="${_wget:-false}"
   389 
   390     # Remove potential left-over from a previous run
   391     rm -f "${tmp}"
   392 
   393     # Some (very old!) FTP server might not support the passive mode, thus
   394     # retry without.
   395     # We also retry a few times, in case there is a transient error (eg. behind
   396     # a dynamic IP that changes during the transfer...)
   397     # With automated download as we are doing, it can be very dangerous to
   398     # continue the downloads. It's far better to simply overwrite the
   399     # destination file.
   400     # Some company networks have firewalls to connect to the internet, but it's
   401     # not easy to detect them, and wget does not timeout by default while
   402     # connecting, so force a global ${CT_CONNECT_TIMEOUT}-second timeout.
   403     # For curl, no good progress indicator is available. So, be silent.
   404     if CT_DoExecLog ALL "${_curl}" --ftp-pasv    --retry 3 --connect-timeout ${CT_CONNECT_TIMEOUT} -L -f -s -o "${tmp}"   "$1"  \
   405     || CT_DoExecLog ALL "${_curl}"               --retry 3 --connect-timeout ${CT_CONNECT_TIMEOUT} -L -f -s -o "${tmp}"   "$1"  \
   406     || CT_DoExecLog ALL "${_wget}" --passive-ftp --tries=3 -T ${CT_CONNECT_TIMEOUT} -nc --progress=dot:binary -O "${tmp}" "$1"  \
   407     || CT_DoExecLog ALL "${_wget}"               --tries=3 -T ${CT_CONNECT_TIMEOUT} -nc --progress=dot:binary -O "${tmp}" "$1"  \
   408     ; then
   409         # One of them succeeded, good!
   410         mv "${tmp}" "${dest}"
   411     else
   412         # Woops...
   413         rm -f "${tmp}"
   414     fi
   415 }
   416 
   417 # This function tries to retrieve a tarball form a local directory
   418 # Usage: CT_GetLocal <basename> [.extension]
   419 CT_GetLocal() {
   420     local basename="$1"
   421     local first_ext="$2"
   422     local ext
   423 
   424     # Do we already have it in *our* tarballs dir?
   425     if ext="$( CT_GetFileExtension "${basename}" ${first_ext} )"; then
   426         CT_DoLog DEBUG "Already have '${basename}'"
   427         return 0
   428     fi
   429 
   430     if [ -n "${CT_LOCAL_TARBALLS_DIR}" ]; then
   431         CT_DoLog DEBUG "Trying to retrieve an already downloaded copy of '${basename}'"
   432         # We'd rather have a bzip2'ed tarball, then gzipped tarball, plain tarball,
   433         # or, as a failover, a file without extension.
   434         for ext in ${first_ext} .tar.bz2 .tar.gz .tgz .tar ''; do
   435             CT_DoLog DEBUG "Trying '${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}'"
   436             if [ -r "${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}" -a \
   437                  "${CT_FORCE_DOWNLOAD}" != "y" ]; then
   438                 CT_DoLog DEBUG "Got '${basename}' from local storage"
   439                 CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}" "${CT_TARBALLS_DIR}/${basename}${ext}"
   440                 return 0
   441             fi
   442         done
   443     fi
   444     return 1
   445 }
   446 
   447 # This function saves the specified to local storage if possible,
   448 # and if so, symlinks it for later usage
   449 # Usage: CT_SaveLocal </full/path/file.name>
   450 CT_SaveLocal() {
   451     local file="$1"
   452     local basename="${file##*/}"
   453 
   454     if [ "${CT_SAVE_TARBALLS}" = "y" ]; then
   455         CT_DoLog EXTRA "Saving '${basename}' to local storage"
   456         # The file may already exist if downloads are forced: remove it first
   457         CT_DoExecLog ALL rm -f "${CT_LOCAL_TARBALLS_DIR}/${basename}"
   458         CT_DoExecLog ALL mv -f "${file}" "${CT_LOCAL_TARBALLS_DIR}"
   459         CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${basename}" "${file}"
   460     fi
   461 }
   462 
   463 # Download the file from one of the URLs passed as argument
   464 # Usage: CT_GetFile <basename> [.extension] <url> [url ...]
   465 CT_GetFile() {
   466     local ext
   467     local url URLS LAN_URLS
   468     local file="$1"
   469     local first_ext
   470     shift
   471     # If next argument starts with a dot, then this is not an URL,
   472     # and we can consider that it is a preferred extension.
   473     case "$1" in
   474         .*) first_ext="$1"
   475             shift
   476             ;;
   477     esac
   478 
   479     # Does it exist localy?
   480     CT_GetLocal "${file}" ${first_ext} && return 0 || true
   481     # No, it does not...
   482 
   483     # Try to retrieve the file
   484     CT_DoLog EXTRA "Retrieving '${file}'"
   485     CT_Pushd "${CT_TARBALLS_DIR}"
   486 
   487     URLS="$@"
   488 
   489     # Add URLs on the LAN mirror
   490     LAN_URLS=
   491     if [ "${CT_USE_MIRROR}" = "y" ]; then
   492         CT_TestOrAbort "Please set the mirror base URL" -n "${CT_MIRROR_BASE_URL}"
   493         LAN_URLS="${LAN_URLS} ${CT_MIRROR_BASE_URL}/${file%-*}"
   494         LAN_URLS="${LAN_URLS} ${CT_MIRROR_BASE_URL}"
   495 
   496         if [ "${CT_PREFER_MIRROR}" = "y" ]; then
   497             CT_DoLog DEBUG "Pre-pending LAN mirror URLs"
   498             URLS="${LAN_URLS} ${URLS}"
   499         else
   500             CT_DoLog DEBUG "Appending LAN mirror URLs"
   501             URLS="${URLS} ${LAN_URLS}"
   502         fi
   503     fi
   504 
   505     # Scan all URLs in turn, and try to grab a tarball from there
   506     # Do *not* try git trees (ext=/.git), this is handled in a specific
   507     # wrapper, below
   508     for ext in ${first_ext} .tar.bz2 .tar.gz .tgz .tar ''; do
   509         # Try all urls in turn
   510         for url in ${URLS}; do
   511             CT_DoLog DEBUG "Trying '${url}/${file}${ext}'"
   512             CT_DoGetFile "${url}/${file}${ext}"
   513             if [ -f "${file}${ext}" ]; then
   514                 CT_DoLog DEBUG "Got '${file}' from the Internet"
   515                 CT_SaveLocal "${CT_TARBALLS_DIR}/${file}${ext}"
   516                 return 0
   517             fi
   518         done
   519     done
   520     CT_Popd
   521 
   522     CT_Abort "Could not retrieve '${file}'."
   523 }
   524 
   525 # Checkout from CVS, and build the associated tarball
   526 # The tarball will be called ${basename}.tar.bz2
   527 # Prerequisite: either the server does not require password,
   528 # or the user must already be logged in.
   529 # 'tag' is the tag to retrieve. Must be specified, but can be empty.
   530 # If dirname is specified, then module will be renamed to dirname
   531 # prior to building the tarball.
   532 # Usage: CT_GetCVS <basename> <url> <module> <tag> [dirname[=subdir]]
   533 # Note: if '=subdir' is given, then it is used instead of 'module'.
   534 CT_GetCVS() {
   535     local basename="$1"
   536     local uri="$2"
   537     local module="$3"
   538     local tag="${4:+-r ${4}}"
   539     local dirname="$5"
   540     local tmp_dir
   541 
   542     # Does it exist localy?
   543     CT_GetLocal "${basename}" && return 0 || true
   544     # No, it does not...
   545 
   546     CT_DoLog EXTRA "Retrieving '${basename}'"
   547 
   548     CT_MktempDir tmp_dir
   549     CT_Pushd "${tmp_dir}"
   550 
   551     CT_DoExecLog ALL cvs -z 9 -d "${uri}" co -P ${tag} "${module}"
   552     if [ -n "${dirname}" ]; then
   553         case "${dirname}" in
   554             *=*)
   555                 CT_DoExecLog DEBUG mv "${dirname#*=}" "${dirname%%=*}"
   556                 CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${dirname%%=*}"
   557                 ;;
   558             *)
   559                 CT_DoExecLog ALL mv "${module}" "${dirname}"
   560                 CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${dirname:-${module}}"
   561                 ;;
   562         esac
   563     fi
   564     CT_SaveLocal "${CT_TARBALLS_DIR}/${basename}.tar.bz2"
   565 
   566     CT_Popd
   567     CT_DoExecLog ALL rm -rf "${tmp_dir}"
   568 }
   569 
   570 # Check out from SVN, and build the associated tarball
   571 # The tarball will be called ${basename}.tar.bz2
   572 # Prerequisite: either the server does not require password,
   573 # or the user must already be logged in.
   574 # 'rev' is the revision to retrieve
   575 # Usage: CT_GetSVN <basename> <url> [rev]
   576 CT_GetSVN() {
   577     local basename="$1"
   578     local uri="$2"
   579     local rev="$3"
   580 
   581     # Does it exist localy?
   582     CT_GetLocal "${basename}" && return 0 || true
   583     # No, it does not...
   584 
   585     CT_DoLog EXTRA "Retrieving '${basename}'"
   586 
   587     CT_MktempDir tmp_dir
   588     CT_Pushd "${tmp_dir}"
   589 
   590     CT_DoExecLog ALL svn export ${rev:+-r ${rev}} "${uri}" "${basename}"
   591     CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${basename}"
   592     CT_SaveLocal "${CT_TARBALLS_DIR}/${basename}.tar.bz2"
   593 
   594     CT_Popd
   595     CT_DoExecLog ALL rm -rf "${tmp_dir}"
   596 }
   597 
   598 # Clone a git tree
   599 # Tries the given URLs in turn until one can get cloned. No tarball will be created.
   600 # Prerequisites: either the server does not require password,
   601 # or the user has already taken any action to authenticate to the server.
   602 # The cloned tree will *not* be stored in the local tarballs dir!
   603 # Usage: CT_GetGit <basename> <url [url ...]>
   604 CT_GetGit() {
   605     local basename="$1"; shift
   606     local url
   607     local cloned=0
   608 
   609     # Do we have it in our tarballs dir?
   610     if [ -d "${CT_TARBALLS_DIR}/${basename}/.git" ]; then
   611         CT_DoLog EXTRA "Updating git tree '${basename}'"
   612         CT_Pushd "${CT_TARBALLS_DIR}/${basename}"
   613         CT_DoExecLog ALL git pull
   614         CT_Popd
   615     else
   616         CT_DoLog EXTRA "Retrieving git tree '${basename}'"
   617         for url in "${@}"; do
   618             CT_DoLog ALL "Trying to clone from '${url}'"
   619             CT_DoForceRmdir "${CT_TARBALLS_DIR}/${basename}"
   620             if git clone "${url}" "${CT_TARBALLS_DIR}/${basename}" 2>&1 |CT_DoLog ALL; then
   621                 cloned=1
   622                 break
   623             fi
   624         done
   625         CT_TestOrAbort "Could not clone '${basename}'" ${cloned} -ne 0
   626     fi
   627 }
   628 
   629 # Extract a tarball
   630 # Some tarballs need to be extracted in specific places. Eg.: glibc addons
   631 # must be extracted in the glibc directory; uCLibc locales must be extracted
   632 # in the extra/locale sub-directory of uClibc. This is taken into account
   633 # by the caller, that did a 'cd' into the correct path before calling us
   634 # and sets nochdir to 'nochdir'.
   635 # Note also that this function handles the git trees!
   636 # Usage: CT_Extract <basename> [nochdir] [options]
   637 # where 'options' are dependent on the source (eg. git branch/tag...)
   638 CT_Extract() {
   639     local nochdir="$1"
   640     local basename
   641     local ext
   642 
   643     if [ "${nochdir}" = "nochdir" ]; then
   644         shift
   645         nochdir="$(pwd)"
   646     else
   647         nochdir="${CT_SRC_DIR}"
   648     fi
   649 
   650     basename="$1"
   651     shift
   652 
   653     if ! ext="$(CT_GetFileExtension "${basename}")"; then
   654       CT_Abort "'${basename}' not found in '${CT_TARBALLS_DIR}'"
   655     fi
   656     local full_file="${CT_TARBALLS_DIR}/${basename}${ext}"
   657 
   658     # Check if already extracted
   659     if [ -e "${CT_SRC_DIR}/.${basename}.extracted" ]; then
   660         CT_DoLog DEBUG "Already extracted '${basename}'"
   661         return 0
   662     fi
   663 
   664     # Check if previously partially extracted
   665     if [ -e "${CT_SRC_DIR}/.${basename}.extracting" ]; then
   666         CT_DoLog ERROR "The '${basename}' sources were partially extracted."
   667         CT_DoLog ERROR "Please remove first:"
   668         CT_DoLog ERROR " - the source dir for '${basename}', in '${CT_SRC_DIR}'"
   669         CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${basename}.extracting'"
   670         CT_Abort "I'll stop now to avoid any carnage..."
   671     fi
   672     CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${basename}.extracting"
   673 
   674     CT_Pushd "${nochdir}"
   675 
   676     CT_DoLog EXTRA "Extracting '${basename}'"
   677     case "${ext}" in
   678         .tar.bz2)     CT_DoExecLog FILE tar xvjf "${full_file}";;
   679         .tar.gz|.tgz) CT_DoExecLog FILE tar xvzf "${full_file}";;
   680         .tar)         CT_DoExecLog FILE tar xvf  "${full_file}";;
   681         /.git)        CT_ExtractGit "${basename}" "${@}";;
   682         *)            CT_Abort "Don't know how to handle '${basename}${ext}': unknown extension";;
   683     esac
   684 
   685     # Don't mark as being extracted for git
   686     case "${ext}" in
   687         /.git)  ;;
   688         *)      CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${basename}.extracted";;
   689     esac
   690     CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/.${basename}.extracting"
   691 
   692     CT_Popd
   693 }
   694 
   695 # Create a working git clone
   696 # Usage: CT_ExtractGit <basename> [ref]
   697 # where 'ref' is the reference to use:
   698 #   the full name of a branch, like "remotes/origin/branch_name"
   699 #   a date as understandable by git, like "YYYY-MM-DD[ hh[:mm[:ss]]]"
   700 #   a tag name
   701 CT_ExtractGit() {
   702     local basename="${1}"
   703     local ref="${2}"
   704     local clone_dir
   705     local ref_type
   706 
   707     # pushd now to be able to get git revlist in case ref is a date
   708     clone_dir="${CT_TARBALLS_DIR}/${basename}"
   709     CT_Pushd "${clone_dir}"
   710 
   711     # What kind of reference is ${ref} ?
   712     if [ -z "${ref}" ]; then
   713         # Don't update the clone, keep as-is
   714         ref_type=none
   715     elif git tag |grep -E "^${ref}$" >/dev/null 2>&1; then
   716         ref_type=tag
   717     elif git branch -a --no-color |grep -E "^. ${ref}$" >/dev/null 2>&1; then
   718         ref_type=branch
   719     elif date -d "${ref}" >/dev/null 2>&1; then
   720         ref_type=date
   721         ref=$(git rev-list -n1 --before="${ref}")
   722     else
   723         CT_Abort "Reference '${ref}' is an incorrect git reference: neither tag, branch nor date"
   724     fi
   725 
   726     CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/${basename}"
   727     CT_DoExecLog ALL ln -sf "${clone_dir}" "${CT_SRC_DIR}/${basename}"
   728 
   729     case "${ref_type}" in
   730         none)   ;;
   731         *)      CT_DoExecLog FILE git checkout "${ref}";;
   732     esac
   733 
   734     CT_Popd
   735 }
   736 
   737 # Patches the specified component
   738 # See CT_Extract, above, for explanations on 'nochdir'
   739 # Usage: CT_Patch [nochdir] <packagename> <packageversion>
   740 # If the package directory is *not* packagename-packageversion, then
   741 # the caller must cd into the proper directory first, and call us
   742 # with nochdir
   743 CT_Patch() {
   744     local nochdir="$1"
   745     local pkgname
   746     local version
   747     local pkgdir
   748     local base_file
   749     local ver_file
   750     local d
   751     local -a patch_dirs
   752     local bundled_patch_dir
   753     local local_patch_dir
   754 
   755     if [ "${nochdir}" = "nochdir" ]; then
   756         shift
   757         pkgname="$1"
   758         version="$2"
   759         pkgdir="${pkgname}-${version}"
   760         nochdir="$(pwd)"
   761     else
   762         pkgname="$1"
   763         version="$2"
   764         pkgdir="${pkgname}-${version}"
   765         nochdir="${CT_SRC_DIR}/${pkgdir}"
   766     fi
   767 
   768     # Check if already patched
   769     if [ -e "${CT_SRC_DIR}/.${pkgdir}.patched" ]; then
   770         CT_DoLog DEBUG "Already patched '${pkgdir}'"
   771         return 0
   772     fi
   773 
   774     # Check if already partially patched
   775     if [ -e "${CT_SRC_DIR}/.${pkgdir}.patching" ]; then
   776         CT_DoLog ERROR "The '${pkgdir}' sources were partially patched."
   777         CT_DoLog ERROR "Please remove first:"
   778         CT_DoLog ERROR " - the source dir for '${pkgdir}', in '${CT_SRC_DIR}'"
   779         CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${pkgdir}.extracted'"
   780         CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${pkgdir}.patching'"
   781         CT_Abort "I'll stop now to avoid any carnage..."
   782     fi
   783     touch "${CT_SRC_DIR}/.${pkgdir}.patching"
   784 
   785     CT_Pushd "${nochdir}"
   786 
   787     CT_DoLog EXTRA "Patching '${pkgdir}'"
   788 
   789     bundled_patch_dir="${CT_LIB_DIR}/patches/${pkgname}/${version}"
   790     local_patch_dir="${CT_LOCAL_PATCH_DIR}/${pkgname}/${version}"
   791 
   792     case "${CT_PATCH_ORDER}" in
   793         bundled)        patch_dirs=("${bundled_patch_dir}");;
   794         local)          patch_dirs=("${local_patch_dir}");;
   795         bundled,local)  patch_dirs=("${bundled_patch_dir}" "${local_patch_dir}");;
   796         local,bundled)  patch_dirs=("${local_patch_dir}" "${bundled_patch_dir}");;
   797         none)           patch_dirs=;;
   798     esac
   799 
   800     for d in "${patch_dirs[@]}"; do
   801         CT_DoLog DEBUG "Looking for patches in '${d}'..."
   802         if [ -n "${d}" -a -d "${d}" ]; then
   803             for p in "${d}"/*.patch; do
   804                 if [ -f "${p}" ]; then
   805                     CT_DoLog DEBUG "Applying patch '${p}'"
   806                     CT_DoExecLog ALL patch --no-backup-if-mismatch -g0 -F1 -p1 -f <"${p}"
   807                 fi
   808             done
   809             if [ "${CT_PATCH_SINGLE}" = "y" ]; then
   810                 break
   811             fi
   812         fi
   813     done
   814 
   815     if [ "${CT_OVERIDE_CONFIG_GUESS_SUB}" = "y" ]; then
   816         CT_DoLog ALL "Overiding config.guess and config.sub"
   817         for cfg in config_guess config_sub; do
   818             eval ${cfg}="${CT_LIB_DIR}/scripts/${cfg/_/.}"
   819             [ -e "${CT_TOP_DIR}/scripts/${cfg/_/.}" ] && eval ${cfg}="${CT_TOP_DIR}/scripts/${cfg/_/.}"
   820             # Can't use CT_DoExecLog because of the '{} \;' to be passed un-mangled to find
   821             find . -type f -name "${cfg/_/.}" -exec cp -v "${!cfg}" {} \; |CT_DoLog ALL
   822         done
   823     fi
   824 
   825     CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${pkgdir}.patched"
   826     CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/.${pkgdir}.patching"
   827 
   828     CT_Popd
   829 }
   830 
   831 # Two wrappers to call config.(guess|sub) either from CT_TOP_DIR or CT_LIB_DIR.
   832 # Those from CT_TOP_DIR, if they exist, will be be more recent than those from CT_LIB_DIR.
   833 CT_DoConfigGuess() {
   834     if [ -x "${CT_TOP_DIR}/scripts/config.guess" ]; then
   835         "${CT_TOP_DIR}/scripts/config.guess"
   836     else
   837         "${CT_LIB_DIR}/scripts/config.guess"
   838     fi
   839 }
   840 
   841 CT_DoConfigSub() {
   842     if [ -x "${CT_TOP_DIR}/scripts/config.sub" ]; then
   843         "${CT_TOP_DIR}/scripts/config.sub" "$@"
   844     else
   845         "${CT_LIB_DIR}/scripts/config.sub" "$@"
   846     fi
   847 }
   848 
   849 # Compute the target tuple from what is provided by the user
   850 # Usage: CT_DoBuildTargetTuple
   851 # In fact this function takes the environment variables to build the target
   852 # tuple. It is needed both by the normal build sequence, as well as the
   853 # sample saving sequence.
   854 CT_DoBuildTargetTuple() {
   855     # Set the endianness suffix, and the default endianness gcc option
   856     case "${CT_ARCH_BE},${CT_ARCH_LE}" in
   857         y,) target_endian_eb=eb
   858             target_endian_el=
   859             CT_ARCH_ENDIAN_CFLAG="-mbig-endian"
   860             CT_ARCH_ENDIAN_LDFLAG="-EB"
   861             ;;
   862         ,y) target_endian_eb=
   863             target_endian_el=el
   864             CT_ARCH_ENDIAN_CFLAG="-mlittle-endian"
   865             CT_ARCH_ENDIAN_LDFLAG="-EL"
   866             ;;
   867     esac
   868 
   869     # Build the default architecture tuple part
   870     CT_TARGET_ARCH="${CT_ARCH}"
   871 
   872     # Set defaults for the system part of the tuple. Can be overriden
   873     # by architecture-specific values.
   874     case "${CT_LIBC}" in
   875         *glibc) CT_TARGET_SYS=gnu;;
   876         uClibc) CT_TARGET_SYS=uclibc;;
   877         *)      CT_TARGET_SYS=elf;;
   878     esac
   879 
   880     # Set the default values for ARCH, ABI, CPU, TUNE, FPU and FLOAT
   881     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
   882     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
   883     [ "${CT_ARCH_ARCH}"     ] && { CT_ARCH_ARCH_CFLAG="-march=${CT_ARCH_ARCH}";  CT_ARCH_WITH_ARCH="--with-arch=${CT_ARCH_ARCH}"; }
   884     [ "${CT_ARCH_ABI}"      ] && { CT_ARCH_ABI_CFLAG="-mabi=${CT_ARCH_ABI}";     CT_ARCH_WITH_ABI="--with-abi=${CT_ARCH_ABI}";    }
   885     [ "${CT_ARCH_CPU}"      ] && { CT_ARCH_CPU_CFLAG="-mcpu=${CT_ARCH_CPU}";     CT_ARCH_WITH_CPU="--with-cpu=${CT_ARCH_CPU}";    }
   886     [ "${CT_ARCH_TUNE}"     ] && { CT_ARCH_TUNE_CFLAG="-mtune=${CT_ARCH_TUNE}";  CT_ARCH_WITH_TUNE="--with-tune=${CT_ARCH_TUNE}"; }
   887     [ "${CT_ARCH_FPU}"      ] && { CT_ARCH_FPU_CFLAG="-mfpu=${CT_ARCH_FPU}";     CT_ARCH_WITH_FPU="--with-fpu=${CT_ARCH_FPU}";    }
   888     [ "${CT_ARCH_FLOAT_SW}" ] && { CT_ARCH_FLOAT_CFLAG="-msoft-float";           CT_ARCH_WITH_FLOAT="--with-float=soft";          }
   889 
   890     # Build the default kernel tuple part
   891     CT_TARGET_KERNEL="${CT_KERNEL}"
   892 
   893     # Overide the default values with the components specific settings
   894     CT_DoArchTupleValues
   895     CT_DoKernelTupleValues
   896 
   897     # Finish the target tuple construction
   898     CT_TARGET="${CT_TARGET_ARCH}"
   899     CT_TARGET="${CT_TARGET}${CT_TARGET_VENDOR:+-${CT_TARGET_VENDOR}}"
   900     CT_TARGET="${CT_TARGET}${CT_TARGET_KERNEL:+-${CT_TARGET_KERNEL}}"
   901     CT_TARGET="${CT_TARGET}${CT_TARGET_SYS:+-${CT_TARGET_SYS}}"
   902 
   903     # Sanity checks
   904     __sed_alias=""
   905     if [ -n "${CT_TARGET_ALIAS_SED_EXPR}" ]; then
   906         __sed_alias=$(echo "${CT_TARGET}" |sed -r -e "${CT_TARGET_ALIAS_SED_EXPR}")
   907     fi
   908     case ":${CT_TARGET_VENDOR}:${CT_TARGET_ALIAS}:${__sed_alias}:" in
   909       :*" "*:*:*:) CT_Abort "Don't use spaces in the vendor string, it breaks things.";;
   910       :*"-"*:*:*:) CT_Abort "Don't use dashes in the vendor string, it breaks things.";;
   911       :*:*" "*:*:) CT_Abort "Don't use spaces in the target alias, it breaks things.";;
   912       :*:*:*" "*:) CT_Abort "Don't use spaces in the target sed transform, it breaks things.";;
   913     esac
   914 
   915     # Canonicalise it
   916     CT_TARGET=$(CT_DoConfigSub "${CT_TARGET}")
   917     # Prepare the target CFLAGS
   918     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ENDIAN_CFLAG}"
   919     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ARCH_CFLAG}"
   920     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ABI_CFLAG}"
   921     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_CPU_CFLAG}"
   922     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_TUNE_CFLAG}"
   923     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FPU_CFLAG}"
   924     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FLOAT_CFLAG}"
   925 
   926     # Now on for the target LDFLAGS
   927     CT_ARCH_TARGET_LDFLAGS="${CT_ARCH_TARGET_LDFLAGS} ${CT_ARCH_ENDIAN_LDFLAG}"
   928 }
   929 
   930 # This function does pause the build until the user strikes "Return"
   931 # Usage: CT_DoPause [optional_message]
   932 CT_DoPause() {
   933     local foo
   934     local message="${1:-Pausing for your pleasure}"
   935     CT_DoLog INFO "${message}"
   936     read -p "Press 'Enter' to continue, or Ctrl-C to stop..." foo >&6
   937     return 0
   938 }
   939 
   940 # This function creates a tarball of the specified directory, but
   941 # only if it exists
   942 # Usage: CT_DoTarballIfExists <dir> <tarball_basename> [extra_tar_options [...]]
   943 CT_DoTarballIfExists() {
   944     local dir="$1"
   945     local tarball="$2"
   946     shift 2
   947     local -a extra_tar_opts=( "$@" )
   948     local -a compress
   949 
   950     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   951         y)  compress=( gzip -c -3 - ); tar_ext=.gz;;
   952         *)  compress=( cat - );        tar_ext=;;
   953     esac
   954 
   955     if [ -d "${dir}" ]; then
   956         CT_DoLog DEBUG "  Saving '${dir}'"
   957         { tar c -C "${dir}" -v -f - "${extra_tar_opts[@]}" .    \
   958           |"${compress[@]}" >"${tarball}.tar${tar_ext}"         ;
   959         } 2>&1 |sed -r -e 's/^/    /;' |CT_DoLog STATE
   960     else
   961         CT_DoLog STATE "  Not saving '${dir}': does not exist"
   962     fi
   963 }
   964 
   965 # This function extracts a tarball to the specified directory, but
   966 # only if the tarball exists
   967 # Usage: CT_DoExtractTarballIfExists <tarball_basename> <dir> [extra_tar_options [...]]
   968 CT_DoExtractTarballIfExists() {
   969     local tarball="$1"
   970     local dir="$2"
   971     shift 2
   972     local -a extra_tar_opts=( "$@" )
   973     local -a uncompress
   974 
   975     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   976         y)  uncompress=( gzip -c -d ); tar_ext=.gz;;
   977         *)  uncompress=( cat );        tar_ext=;;
   978     esac
   979 
   980     if [ -f "${tarball}.tar${tar_ext}" ]; then
   981         CT_DoLog DEBUG "  Restoring '${dir}'"
   982         CT_DoForceRmdir "${dir}"
   983         CT_DoExecLog DEBUG mkdir -p "${dir}"
   984         { "${uncompress[@]}" "${tarball}.tar${tar_ext}"     \
   985           |tar x -C "${dir}" -v -f - "${extra_tar_opts[@]}" ;
   986         } 2>&1 |sed -r -e 's/^/    /;' |CT_DoLog STATE
   987     else
   988         CT_DoLog STATE "  Not restoring '${dir}': does not exist"
   989     fi
   990 }
   991 
   992 # This function saves the state of the toolchain to be able to restart
   993 # at any one point
   994 # Usage: CT_DoSaveState <next_step_name>
   995 CT_DoSaveState() {
   996 	[ "${CT_DEBUG_CT_SAVE_STEPS}" = "y" ] || return 0
   997     local state_name="$1"
   998     local state_dir="${CT_STATE_DIR}/${state_name}"
   999 
  1000     # Log this to the log level required by the user
  1001     CT_DoLog ${CT_LOG_LEVEL_MAX} "Saving state to restart at step '${state_name}'..."
  1002 
  1003     rm -rf "${state_dir}"
  1004     mkdir -p "${state_dir}"
  1005 
  1006     CT_DoLog STATE "  Saving environment and aliases"
  1007     # We must omit shell functions, and some specific bash variables
  1008     # that break when restoring the environment, later. We could do
  1009     # all the processing in the awk script, but a sed is easier...
  1010     set |awk '
  1011               BEGIN { _p = 1; }
  1012               $0~/^[^ ]+ \(\)/ { _p = 0; }
  1013               _p == 1
  1014               $0 == "}" { _p = 1; }
  1015               ' |sed -r -e '/^BASH_(ARGC|ARGV|LINENO|SOURCE|VERSINFO)=/d;
  1016                            /^(UID|EUID)=/d;
  1017                            /^(FUNCNAME|GROUPS|PPID|SHELLOPTS)=/d;' >"${state_dir}/env.sh"
  1018 
  1019     if [ "${CT_COMPLIBS_BACKUP}" = "y" ]; then
  1020         CT_DoTarballIfExists "${CT_COMPLIBS_DIR}" "${state_dir}/complibs_dir"
  1021     fi
  1022     CT_DoTarballIfExists "${CT_BUILDTOOLS_PREFIX_DIR}" "${state_dir}/buildtools_dir"
  1023     CT_DoTarballIfExists "${CT_CONFIG_DIR}" "${state_dir}/config_dir"
  1024     CT_DoTarballIfExists "${CT_CC_CORE_STATIC_PREFIX_DIR}" "${state_dir}/cc_core_static_prefix_dir"
  1025     CT_DoTarballIfExists "${CT_CC_CORE_SHARED_PREFIX_DIR}" "${state_dir}/cc_core_shared_prefix_dir"
  1026     CT_DoTarballIfExists "${CT_PREFIX_DIR}" "${state_dir}/prefix_dir" --exclude '*.log'
  1027 
  1028     CT_DoLog STATE "  Saving log file"
  1029     exec >/dev/null
  1030     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
  1031         y)  gzip -3 -c "${tmp_log_file}"  >"${state_dir}/log.gz";;
  1032         *)  cat "${tmp_log_file}" >"${state_dir}/log";;
  1033     esac
  1034     exec >>"${tmp_log_file}"
  1035 }
  1036 
  1037 # This function restores a previously saved state
  1038 # Usage: CT_DoLoadState <state_name>
  1039 CT_DoLoadState(){
  1040     local state_name="$1"
  1041     local state_dir="${CT_STATE_DIR}/${state_name}"
  1042     local old_RESTART="${CT_RESTART}"
  1043     local old_STOP="${CT_STOP}"
  1044 
  1045     CT_TestOrAbort "The previous build did not reach the point where it could be restarted at '${CT_RESTART}'" -d "${state_dir}"
  1046 
  1047     # We need to do something special with the log file!
  1048     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
  1049         exec >"${state_dir}/tail.log"
  1050     fi
  1051 
  1052     # Log this to the log level required by the user
  1053     CT_DoLog ${CT_LOG_LEVEL_MAX} "Restoring state at step '${state_name}', as requested."
  1054 
  1055     CT_DoExtractTarballIfExists "${state_dir}/prefix_dir" "${CT_PREFIX_DIR}"
  1056     CT_DoExtractTarballIfExists "${state_dir}/cc_core_shared_prefix_dir" "${CT_CC_CORE_SHARED_PREFIX_DIR}"
  1057     CT_DoExtractTarballIfExists "${state_dir}/cc_core_static_prefix_dir" "${CT_CC_CORE_STATIC_PREFIX_DIR}"
  1058     CT_DoExtractTarballIfExists "${state_dir}/config_dir" "${CT_CONFIG_DIR}"
  1059     CT_DoExtractTarballIfExists "${state_dir}/buildtools_dir" "${CT_BUILDTOOLS_PREFIX_DIR}"
  1060     if [ "${CT_COMPLIBS_BACKUP}" = "y" ]; then
  1061         CT_DoExtractTarballIfExists "${state_dir}/complibs_dir" "${CT_COMPLIBS_DIR}"
  1062     fi
  1063 
  1064     # Restore the environment, discarding any error message
  1065     # (for example, read-only bash internals)
  1066     CT_DoLog STATE "  Restoring environment"
  1067     . "${state_dir}/env.sh" >/dev/null 2>&1 || true
  1068 
  1069     # Restore the new RESTART and STOP steps
  1070     CT_RESTART="${old_RESTART}"
  1071     CT_STOP="${old_STOP}"
  1072     unset old_stop old_restart
  1073 
  1074     CT_DoLog STATE "  Restoring log file"
  1075     exec >/dev/null
  1076     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
  1077         y)  zcat "${state_dir}/log.gz" >"${tmp_log_file}";;
  1078         *)  cat "${state_dir}/log" >"${tmp_log_file}";;
  1079     esac
  1080     cat "${state_dir}/tail.log" >>"${tmp_log_file}"
  1081     exec >>"${tmp_log_file}"
  1082     rm -f "${state_dir}/tail.log"
  1083 }