scripts/functions
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Thu Apr 28 00:13:41 2011 +0200 (2011-04-28)
changeset 2412 20edcd78cf67
parent 2383 bb6e2df2427f
child 2492 b6495ef0193c
permissions -rw-r--r--
scripts/addToolsVersion: versions can be either in the .in or the .in.2

The components have their version selection handled either in the .in
file or the .in.2 file. Handle both cases.

Also, when dumping an existing version, keep the user's grep options
(ie. do override neither options nor colors).

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