scripts/functions
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Thu Dec 23 20:43:32 2010 +0100 (2010-12-23)
changeset 2307 2efd46963086
parent 2280 3d480d12124e
child 2308 911b3a703497
permissions -rw-r--r--
buildtools: move to working directory

There is absolutely *no* reason for the buildtools (wrappers to gcc, g++,
as, ld... for the local machine) to be in the toolchain directory. Moreover,
they are removed after the build completes.

Move them out of the toolchain directory, and into the build directory (but
yet the part specific to the current toolchain). This means we no longer
need to explicitly remove them either, BTW, but we need to save/restore them
for the restart feature.

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