scripts/functions
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Tue May 31 00:20:44 2011 +0200 (2011-05-31)
changeset 2495 98b02f85db29
parent 2493 b0eac0056942
child 2501 5f2d85ceb423
permissions -rw-r--r--
libc/eglibc: use generic SVN functions

eglibc is only available from SVN. The script currently calls svn
in its own tortuous and convoluted way.

Use the egeneric SVN extract functions, and sinplify the eglibc
download function.

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 
   534     URLS="$@"
   535 
   536     # Add URLs on the LAN mirror
   537     LAN_URLS=
   538     if [ "${CT_USE_MIRROR}" = "y" ]; then
   539         CT_TestOrAbort "Please set the mirror base URL" -n "${CT_MIRROR_BASE_URL}"
   540         LAN_URLS="${LAN_URLS} ${CT_MIRROR_BASE_URL}/${file%-*}"
   541         LAN_URLS="${LAN_URLS} ${CT_MIRROR_BASE_URL}"
   542 
   543         if [ "${CT_PREFER_MIRROR}" = "y" ]; then
   544             CT_DoLog DEBUG "Pre-pending LAN mirror URLs"
   545             URLS="${LAN_URLS} ${URLS}"
   546         else
   547             CT_DoLog DEBUG "Appending LAN mirror URLs"
   548             URLS="${URLS} ${LAN_URLS}"
   549         fi
   550     fi
   551 
   552     # Scan all URLs in turn, and try to grab a tarball from there
   553     # Do *not* try git trees (ext=/.git), this is handled in a specific
   554     # wrapper, below
   555     for ext in ${first_ext} .tar.bz2 .tar.gz .tgz .tar ''; do
   556         # Try all urls in turn
   557         for url in ${URLS}; do
   558             CT_DoLog DEBUG "Trying '${url}/${file}${ext}'"
   559             CT_DoGetFile "${url}/${file}${ext}"
   560             if [ -f "${CT_TARBALLS_DIR}/${file}${ext}" ]; then
   561                 CT_DoLog DEBUG "Got '${file}' from the Internet"
   562                 CT_SaveLocal "${CT_TARBALLS_DIR}/${file}${ext}"
   563                 return 0
   564             fi
   565         done
   566     done
   567 
   568     # Just warn, someone may want to catch and handle the error
   569     # (eg. glibc/eglibc add-ons can be missing).
   570     CT_DoLog WARN "Could not retrieve '${file}'."
   571     return 1
   572 }
   573 
   574 # Checkout from CVS, and build the associated tarball
   575 # The tarball will be called ${basename}.tar.bz2
   576 # Prerequisite: either the server does not require password,
   577 # or the user must already be logged in.
   578 # 'tag' is the tag to retrieve. Must be specified, but can be empty.
   579 # If dirname is specified, then module will be renamed to dirname
   580 # prior to building the tarball.
   581 # Usage: CT_GetCVS <basename> <url> <module> <tag> [dirname[=subdir]]
   582 # Note: if '=subdir' is given, then it is used instead of 'module'.
   583 CT_GetCVS() {
   584     local basename="$1"
   585     local uri="$2"
   586     local module="$3"
   587     local tag="${4:+-r ${4}}"
   588     local dirname="$5"
   589     local tmp_dir
   590 
   591     # Does it exist localy?
   592     CT_GetLocal "${basename}" && return 0 || true
   593     # No, it does not...
   594 
   595     CT_DoLog EXTRA "Retrieving '${basename}'"
   596 
   597     CT_MktempDir tmp_dir
   598     CT_Pushd "${tmp_dir}"
   599 
   600     CT_DoExecLog ALL cvs -z 9 -d "${uri}" co -P ${tag} "${module}"
   601     if [ -n "${dirname}" ]; then
   602         case "${dirname}" in
   603             *=*)
   604                 CT_DoExecLog DEBUG mv "${dirname#*=}" "${dirname%%=*}"
   605                 CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${dirname%%=*}"
   606                 ;;
   607             *)
   608                 CT_DoExecLog ALL mv "${module}" "${dirname}"
   609                 CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${dirname:-${module}}"
   610                 ;;
   611         esac
   612     fi
   613     CT_SaveLocal "${CT_TARBALLS_DIR}/${basename}.tar.bz2"
   614 
   615     CT_Popd
   616     CT_DoExecLog ALL rm -rf "${tmp_dir}"
   617 }
   618 
   619 # Check out from SVN, and build the associated tarball
   620 # The tarball will be called ${basename}.tar.bz2
   621 # Prerequisite: either the server does not require password,
   622 # or the user must already be logged in.
   623 # 'rev' is the revision to retrieve
   624 # Usage: CT_GetSVN <basename> <url> [rev]
   625 CT_GetSVN() {
   626     local basename="$1"
   627     local uri="$2"
   628     local rev="$3"
   629 
   630     # Does it exist localy?
   631     CT_GetLocal "${basename}" && return 0 || true
   632     # No, it does not...
   633 
   634     CT_DoLog EXTRA "Retrieving '${basename}'"
   635 
   636     CT_MktempDir tmp_dir
   637     CT_Pushd "${tmp_dir}"
   638 
   639     if ! CT_DoExecLog ALL svn export ${rev:+-r ${rev}} "${uri}" "${basename}"; then
   640         CT_DoLog WARN "Could not retrieve '${basename}'"
   641         return 1
   642     fi
   643     CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${basename}"
   644     CT_SaveLocal "${CT_TARBALLS_DIR}/${basename}.tar.bz2"
   645 
   646     CT_Popd
   647     CT_DoExecLog ALL rm -rf "${tmp_dir}"
   648 }
   649 
   650 # Clone a git tree
   651 # Tries the given URLs in turn until one can get cloned. No tarball will be created.
   652 # Prerequisites: either the server does not require password,
   653 # or the user has already taken any action to authenticate to the server.
   654 # The cloned tree will *not* be stored in the local tarballs dir!
   655 # Usage: CT_GetGit <basename> <url [url ...]>
   656 CT_GetGit() {
   657     local basename="$1"; shift
   658     local url
   659     local cloned=0
   660 
   661     # Do we have it in our tarballs dir?
   662     if [ -d "${CT_TARBALLS_DIR}/${basename}/.git" ]; then
   663         CT_DoLog EXTRA "Updating git tree '${basename}'"
   664         CT_Pushd "${CT_TARBALLS_DIR}/${basename}"
   665         CT_DoExecLog ALL git pull
   666         CT_Popd
   667     else
   668         CT_DoLog EXTRA "Retrieving git tree '${basename}'"
   669         for url in "${@}"; do
   670             CT_DoLog ALL "Trying to clone from '${url}'"
   671             CT_DoForceRmdir "${CT_TARBALLS_DIR}/${basename}"
   672             if git clone "${url}" "${CT_TARBALLS_DIR}/${basename}" 2>&1 |CT_DoLog ALL; then
   673                 cloned=1
   674                 break
   675             fi
   676         done
   677         CT_TestOrAbort "Could not clone '${basename}'" ${cloned} -ne 0
   678     fi
   679 }
   680 
   681 # Extract a tarball
   682 # Some tarballs need to be extracted in specific places. Eg.: glibc addons
   683 # must be extracted in the glibc directory; uCLibc locales must be extracted
   684 # in the extra/locale sub-directory of uClibc. This is taken into account
   685 # by the caller, that did a 'cd' into the correct path before calling us
   686 # and sets nochdir to 'nochdir'.
   687 # Note also that this function handles the git trees!
   688 # Usage: CT_Extract <basename> [nochdir] [options]
   689 # where 'options' are dependent on the source (eg. git branch/tag...)
   690 CT_Extract() {
   691     local nochdir="$1"
   692     local basename
   693     local ext
   694 
   695     if [ "${nochdir}" = "nochdir" ]; then
   696         shift
   697         nochdir="$(pwd)"
   698     else
   699         nochdir="${CT_SRC_DIR}"
   700     fi
   701 
   702     basename="$1"
   703     shift
   704 
   705     if ! ext="$(CT_GetFileExtension "${basename}")"; then
   706         CT_DoLog WARN "'${basename}' not found in '${CT_TARBALLS_DIR}'"
   707         return 1
   708     fi
   709     local full_file="${CT_TARBALLS_DIR}/${basename}${ext}"
   710 
   711     # Check if already extracted
   712     if [ -e "${CT_SRC_DIR}/.${basename}.extracted" ]; then
   713         CT_DoLog DEBUG "Already extracted '${basename}'"
   714         return 0
   715     fi
   716 
   717     # Check if previously partially extracted
   718     if [ -e "${CT_SRC_DIR}/.${basename}.extracting" ]; then
   719         CT_DoLog ERROR "The '${basename}' sources were partially extracted."
   720         CT_DoLog ERROR "Please remove first:"
   721         CT_DoLog ERROR " - the source dir for '${basename}', in '${CT_SRC_DIR}'"
   722         CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${basename}.extracting'"
   723         CT_Abort "I'll stop now to avoid any carnage..."
   724     fi
   725     CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${basename}.extracting"
   726 
   727     CT_Pushd "${nochdir}"
   728 
   729     CT_DoLog EXTRA "Extracting '${basename}'"
   730     case "${ext}" in
   731         .tar.bz2)     CT_DoExecLog FILE tar xvjf "${full_file}";;
   732         .tar.gz|.tgz) CT_DoExecLog FILE tar xvzf "${full_file}";;
   733         .tar)         CT_DoExecLog FILE tar xvf  "${full_file}";;
   734         /.git)        CT_ExtractGit "${basename}" "${@}";;
   735         *)            CT_DoLog WARN "Don't know how to handle '${basename}${ext}': unknown extension"
   736                       return 1
   737                       ;;
   738     esac
   739 
   740     # Don't mark as being extracted for git
   741     case "${ext}" in
   742         /.git)  ;;
   743         *)      CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${basename}.extracted";;
   744     esac
   745     CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/.${basename}.extracting"
   746 
   747     CT_Popd
   748 }
   749 
   750 # Create a working git clone
   751 # Usage: CT_ExtractGit <basename> [ref]
   752 # where 'ref' is the reference to use:
   753 #   the full name of a branch, like "remotes/origin/branch_name"
   754 #   a date as understandable by git, like "YYYY-MM-DD[ hh[:mm[:ss]]]"
   755 #   a tag name
   756 CT_ExtractGit() {
   757     local basename="${1}"
   758     local ref="${2}"
   759     local clone_dir
   760     local ref_type
   761 
   762     # pushd now to be able to get git revlist in case ref is a date
   763     clone_dir="${CT_TARBALLS_DIR}/${basename}"
   764     CT_Pushd "${clone_dir}"
   765 
   766     # What kind of reference is ${ref} ?
   767     if [ -z "${ref}" ]; then
   768         # Don't update the clone, keep as-is
   769         ref_type=none
   770     elif git tag |grep -E "^${ref}$" >/dev/null 2>&1; then
   771         ref_type=tag
   772     elif git branch -a --no-color |grep -E "^. ${ref}$" >/dev/null 2>&1; then
   773         ref_type=branch
   774     elif date -d "${ref}" >/dev/null 2>&1; then
   775         ref_type=date
   776         ref=$(git rev-list -n1 --before="${ref}")
   777     else
   778         CT_Abort "Reference '${ref}' is an incorrect git reference: neither tag, branch nor date"
   779     fi
   780 
   781     CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/${basename}"
   782     CT_DoExecLog ALL ln -sf "${clone_dir}" "${CT_SRC_DIR}/${basename}"
   783 
   784     case "${ref_type}" in
   785         none)   ;;
   786         *)      CT_DoExecLog FILE git checkout "${ref}";;
   787     esac
   788 
   789     CT_Popd
   790 }
   791 
   792 # Patches the specified component
   793 # See CT_Extract, above, for explanations on 'nochdir'
   794 # Usage: CT_Patch [nochdir] <packagename> <packageversion>
   795 # If the package directory is *not* packagename-packageversion, then
   796 # the caller must cd into the proper directory first, and call us
   797 # with nochdir
   798 CT_Patch() {
   799     local nochdir="$1"
   800     local pkgname
   801     local version
   802     local pkgdir
   803     local base_file
   804     local ver_file
   805     local d
   806     local -a patch_dirs
   807     local bundled_patch_dir
   808     local local_patch_dir
   809 
   810     if [ "${nochdir}" = "nochdir" ]; then
   811         shift
   812         pkgname="$1"
   813         version="$2"
   814         pkgdir="${pkgname}-${version}"
   815         nochdir="$(pwd)"
   816     else
   817         pkgname="$1"
   818         version="$2"
   819         pkgdir="${pkgname}-${version}"
   820         nochdir="${CT_SRC_DIR}/${pkgdir}"
   821     fi
   822 
   823     # Check if already patched
   824     if [ -e "${CT_SRC_DIR}/.${pkgdir}.patched" ]; then
   825         CT_DoLog DEBUG "Already patched '${pkgdir}'"
   826         return 0
   827     fi
   828 
   829     # Check if already partially patched
   830     if [ -e "${CT_SRC_DIR}/.${pkgdir}.patching" ]; then
   831         CT_DoLog ERROR "The '${pkgdir}' sources were partially patched."
   832         CT_DoLog ERROR "Please remove first:"
   833         CT_DoLog ERROR " - the source dir for '${pkgdir}', in '${CT_SRC_DIR}'"
   834         CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${pkgdir}.extracted'"
   835         CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${pkgdir}.patching'"
   836         CT_Abort "I'll stop now to avoid any carnage..."
   837     fi
   838     touch "${CT_SRC_DIR}/.${pkgdir}.patching"
   839 
   840     CT_Pushd "${nochdir}"
   841 
   842     CT_DoLog EXTRA "Patching '${pkgdir}'"
   843 
   844     bundled_patch_dir="${CT_LIB_DIR}/patches/${pkgname}/${version}"
   845     local_patch_dir="${CT_LOCAL_PATCH_DIR}/${pkgname}/${version}"
   846 
   847     case "${CT_PATCH_ORDER}" in
   848         bundled)        patch_dirs=("${bundled_patch_dir}");;
   849         local)          patch_dirs=("${local_patch_dir}");;
   850         bundled,local)  patch_dirs=("${bundled_patch_dir}" "${local_patch_dir}");;
   851         local,bundled)  patch_dirs=("${local_patch_dir}" "${bundled_patch_dir}");;
   852         none)           patch_dirs=;;
   853     esac
   854 
   855     for d in "${patch_dirs[@]}"; do
   856         CT_DoLog DEBUG "Looking for patches in '${d}'..."
   857         if [ -n "${d}" -a -d "${d}" ]; then
   858             for p in "${d}"/*.patch; do
   859                 if [ -f "${p}" ]; then
   860                     CT_DoLog DEBUG "Applying patch '${p}'"
   861                     CT_DoExecLog ALL patch --no-backup-if-mismatch -g0 -F1 -p1 -f <"${p}"
   862                 fi
   863             done
   864             if [ "${CT_PATCH_SINGLE}" = "y" ]; then
   865                 break
   866             fi
   867         fi
   868     done
   869 
   870     if [ "${CT_OVERIDE_CONFIG_GUESS_SUB}" = "y" ]; then
   871         CT_DoLog ALL "Overiding config.guess and config.sub"
   872         for cfg in config_guess config_sub; do
   873             eval ${cfg}="${CT_LIB_DIR}/scripts/${cfg/_/.}"
   874             [ -e "${CT_TOP_DIR}/scripts/${cfg/_/.}" ] && eval ${cfg}="${CT_TOP_DIR}/scripts/${cfg/_/.}"
   875             # Can't use CT_DoExecLog because of the '{} \;' to be passed un-mangled to find
   876             find . -type f -name "${cfg/_/.}" -exec cp -v "${!cfg}" {} \; |CT_DoLog ALL
   877         done
   878     fi
   879 
   880     CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${pkgdir}.patched"
   881     CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/.${pkgdir}.patching"
   882 
   883     CT_Popd
   884 }
   885 
   886 # Two wrappers to call config.(guess|sub) either from CT_TOP_DIR or CT_LIB_DIR.
   887 # Those from CT_TOP_DIR, if they exist, will be be more recent than those from CT_LIB_DIR.
   888 CT_DoConfigGuess() {
   889     if [ -x "${CT_TOP_DIR}/scripts/config.guess" ]; then
   890         "${CT_TOP_DIR}/scripts/config.guess"
   891     else
   892         "${CT_LIB_DIR}/scripts/config.guess"
   893     fi
   894 }
   895 
   896 CT_DoConfigSub() {
   897     if [ -x "${CT_TOP_DIR}/scripts/config.sub" ]; then
   898         "${CT_TOP_DIR}/scripts/config.sub" "$@"
   899     else
   900         "${CT_LIB_DIR}/scripts/config.sub" "$@"
   901     fi
   902 }
   903 
   904 # Compute the target tuple from what is provided by the user
   905 # Usage: CT_DoBuildTargetTuple
   906 # In fact this function takes the environment variables to build the target
   907 # tuple. It is needed both by the normal build sequence, as well as the
   908 # sample saving sequence.
   909 CT_DoBuildTargetTuple() {
   910     # Set the endianness suffix, and the default endianness gcc option
   911     case "${CT_ARCH_BE},${CT_ARCH_LE}" in
   912         y,) target_endian_eb=eb
   913             target_endian_el=
   914             CT_ARCH_ENDIAN_CFLAG="-mbig-endian"
   915             CT_ARCH_ENDIAN_LDFLAG="-EB"
   916             ;;
   917         ,y) target_endian_eb=
   918             target_endian_el=el
   919             CT_ARCH_ENDIAN_CFLAG="-mlittle-endian"
   920             CT_ARCH_ENDIAN_LDFLAG="-EL"
   921             ;;
   922     esac
   923 
   924     # Build the default architecture tuple part
   925     CT_TARGET_ARCH="${CT_ARCH}"
   926 
   927     # Set defaults for the system part of the tuple. Can be overriden
   928     # by architecture-specific values.
   929     case "${CT_LIBC}" in
   930         *glibc) CT_TARGET_SYS=gnu;;
   931         uClibc) CT_TARGET_SYS=uclibc;;
   932         *)      CT_TARGET_SYS=elf;;
   933     esac
   934 
   935     # Set the default values for ARCH, ABI, CPU, TUNE, FPU and FLOAT
   936     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
   937     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
   938     [ "${CT_ARCH_ARCH}"     ] && { CT_ARCH_ARCH_CFLAG="-march=${CT_ARCH_ARCH}";  CT_ARCH_WITH_ARCH="--with-arch=${CT_ARCH_ARCH}"; }
   939     [ "${CT_ARCH_ABI}"      ] && { CT_ARCH_ABI_CFLAG="-mabi=${CT_ARCH_ABI}";     CT_ARCH_WITH_ABI="--with-abi=${CT_ARCH_ABI}";    }
   940     [ "${CT_ARCH_CPU}"      ] && { CT_ARCH_CPU_CFLAG="-mcpu=${CT_ARCH_CPU}";     CT_ARCH_WITH_CPU="--with-cpu=${CT_ARCH_CPU}";    }
   941     [ "${CT_ARCH_TUNE}"     ] && { CT_ARCH_TUNE_CFLAG="-mtune=${CT_ARCH_TUNE}";  CT_ARCH_WITH_TUNE="--with-tune=${CT_ARCH_TUNE}"; }
   942     [ "${CT_ARCH_FPU}"      ] && { CT_ARCH_FPU_CFLAG="-mfpu=${CT_ARCH_FPU}";     CT_ARCH_WITH_FPU="--with-fpu=${CT_ARCH_FPU}";    }
   943     [ "${CT_ARCH_FLOAT_SW}" ] && { CT_ARCH_FLOAT_CFLAG="-msoft-float";           CT_ARCH_WITH_FLOAT="--with-float=soft";          }
   944 
   945     # Build the default kernel tuple part
   946     CT_TARGET_KERNEL="${CT_KERNEL}"
   947 
   948     # Overide the default values with the components specific settings
   949     CT_DoArchTupleValues
   950     CT_DoKernelTupleValues
   951 
   952     # Finish the target tuple construction
   953     CT_TARGET="${CT_TARGET_ARCH}"
   954     CT_TARGET="${CT_TARGET}${CT_TARGET_VENDOR:+-${CT_TARGET_VENDOR}}"
   955     CT_TARGET="${CT_TARGET}${CT_TARGET_KERNEL:+-${CT_TARGET_KERNEL}}"
   956     CT_TARGET="${CT_TARGET}${CT_TARGET_SYS:+-${CT_TARGET_SYS}}"
   957 
   958     # Sanity checks
   959     __sed_alias=""
   960     if [ -n "${CT_TARGET_ALIAS_SED_EXPR}" ]; then
   961         __sed_alias=$(echo "${CT_TARGET}" |sed -r -e "${CT_TARGET_ALIAS_SED_EXPR}")
   962     fi
   963     case ":${CT_TARGET_VENDOR}:${CT_TARGET_ALIAS}:${__sed_alias}:" in
   964       :*" "*:*:*:) CT_Abort "Don't use spaces in the vendor string, it breaks things.";;
   965       :*"-"*:*:*:) CT_Abort "Don't use dashes in the vendor string, it breaks things.";;
   966       :*:*" "*:*:) CT_Abort "Don't use spaces in the target alias, it breaks things.";;
   967       :*:*:*" "*:) CT_Abort "Don't use spaces in the target sed transform, it breaks things.";;
   968     esac
   969 
   970     # Canonicalise it
   971     CT_TARGET=$(CT_DoConfigSub "${CT_TARGET}")
   972     # Prepare the target CFLAGS
   973     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ENDIAN_CFLAG}"
   974     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ARCH_CFLAG}"
   975     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ABI_CFLAG}"
   976     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_CPU_CFLAG}"
   977     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_TUNE_CFLAG}"
   978     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FPU_CFLAG}"
   979     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FLOAT_CFLAG}"
   980 
   981     # Now on for the target LDFLAGS
   982     CT_ARCH_TARGET_LDFLAGS="${CT_ARCH_TARGET_LDFLAGS} ${CT_ARCH_ENDIAN_LDFLAG}"
   983 }
   984 
   985 # This function does pause the build until the user strikes "Return"
   986 # Usage: CT_DoPause [optional_message]
   987 CT_DoPause() {
   988     local foo
   989     local message="${1:-Pausing for your pleasure}"
   990     CT_DoLog INFO "${message}"
   991     read -p "Press 'Enter' to continue, or Ctrl-C to stop..." foo >&6
   992     return 0
   993 }
   994 
   995 # This function creates a tarball of the specified directory, but
   996 # only if it exists
   997 # Usage: CT_DoTarballIfExists <dir> <tarball_basename> [extra_tar_options [...]]
   998 CT_DoTarballIfExists() {
   999     local dir="$1"
  1000     local tarball="$2"
  1001     shift 2
  1002     local -a extra_tar_opts=( "$@" )
  1003     local -a compress
  1004 
  1005     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
  1006         y)  compress=( gzip -c -3 - ); tar_ext=.gz;;
  1007         *)  compress=( cat - );        tar_ext=;;
  1008     esac
  1009 
  1010     if [ -d "${dir}" ]; then
  1011         CT_DoLog DEBUG "  Saving '${dir}'"
  1012         { tar c -C "${dir}" -v -f - "${extra_tar_opts[@]}" .    \
  1013           |"${compress[@]}" >"${tarball}.tar${tar_ext}"         ;
  1014         } 2>&1 |sed -r -e 's/^/    /;' |CT_DoLog STATE
  1015     else
  1016         CT_DoLog STATE "  Not saving '${dir}': does not exist"
  1017     fi
  1018 }
  1019 
  1020 # This function extracts a tarball to the specified directory, but
  1021 # only if the tarball exists
  1022 # Usage: CT_DoExtractTarballIfExists <tarball_basename> <dir> [extra_tar_options [...]]
  1023 CT_DoExtractTarballIfExists() {
  1024     local tarball="$1"
  1025     local dir="$2"
  1026     shift 2
  1027     local -a extra_tar_opts=( "$@" )
  1028     local -a uncompress
  1029 
  1030     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
  1031         y)  uncompress=( gzip -c -d ); tar_ext=.gz;;
  1032         *)  uncompress=( cat );        tar_ext=;;
  1033     esac
  1034 
  1035     if [ -f "${tarball}.tar${tar_ext}" ]; then
  1036         CT_DoLog DEBUG "  Restoring '${dir}'"
  1037         CT_DoForceRmdir "${dir}"
  1038         CT_DoExecLog DEBUG mkdir -p "${dir}"
  1039         { "${uncompress[@]}" "${tarball}.tar${tar_ext}"     \
  1040           |tar x -C "${dir}" -v -f - "${extra_tar_opts[@]}" ;
  1041         } 2>&1 |sed -r -e 's/^/    /;' |CT_DoLog STATE
  1042     else
  1043         CT_DoLog STATE "  Not restoring '${dir}': does not exist"
  1044     fi
  1045 }
  1046 
  1047 # This function saves the state of the toolchain to be able to restart
  1048 # at any one point
  1049 # Usage: CT_DoSaveState <next_step_name>
  1050 CT_DoSaveState() {
  1051 	[ "${CT_DEBUG_CT_SAVE_STEPS}" = "y" ] || return 0
  1052     local state_name="$1"
  1053     local state_dir="${CT_STATE_DIR}/${state_name}"
  1054 
  1055     # Log this to the log level required by the user
  1056     CT_DoLog ${CT_LOG_LEVEL_MAX} "Saving state to restart at step '${state_name}'..."
  1057 
  1058     rm -rf "${state_dir}"
  1059     mkdir -p "${state_dir}"
  1060 
  1061     CT_DoLog STATE "  Saving environment and aliases"
  1062     # We must omit shell functions, and some specific bash variables
  1063     # that break when restoring the environment, later. We could do
  1064     # all the processing in the awk script, but a sed is easier...
  1065     set |awk '
  1066               BEGIN { _p = 1; }
  1067               $0~/^[^ ]+ \(\)/ { _p = 0; }
  1068               _p == 1
  1069               $0 == "}" { _p = 1; }
  1070               ' |sed -r -e '/^BASH_(ARGC|ARGV|LINENO|SOURCE|VERSINFO)=/d;
  1071                            /^(UID|EUID)=/d;
  1072                            /^(FUNCNAME|GROUPS|PPID|SHELLOPTS)=/d;' >"${state_dir}/env.sh"
  1073 
  1074     CT_DoTarballIfExists "${CT_BUILDTOOLS_PREFIX_DIR}" "${state_dir}/buildtools_dir"
  1075     CT_DoTarballIfExists "${CT_CONFIG_DIR}" "${state_dir}/config_dir"
  1076     CT_DoTarballIfExists "${CT_CC_CORE_STATIC_PREFIX_DIR}" "${state_dir}/cc_core_static_prefix_dir"
  1077     CT_DoTarballIfExists "${CT_CC_CORE_SHARED_PREFIX_DIR}" "${state_dir}/cc_core_shared_prefix_dir"
  1078     CT_DoTarballIfExists "${CT_PREFIX_DIR}" "${state_dir}/prefix_dir" --exclude '*.log'
  1079 
  1080     CT_DoLog STATE "  Saving log file"
  1081     exec >/dev/null
  1082     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
  1083         y)  gzip -3 -c "${tmp_log_file}"  >"${state_dir}/log.gz";;
  1084         *)  cat "${tmp_log_file}" >"${state_dir}/log";;
  1085     esac
  1086     exec >>"${tmp_log_file}"
  1087 }
  1088 
  1089 # This function restores a previously saved state
  1090 # Usage: CT_DoLoadState <state_name>
  1091 CT_DoLoadState(){
  1092     local state_name="$1"
  1093     local state_dir="${CT_STATE_DIR}/${state_name}"
  1094     local old_RESTART="${CT_RESTART}"
  1095     local old_STOP="${CT_STOP}"
  1096 
  1097     CT_TestOrAbort "The previous build did not reach the point where it could be restarted at '${CT_RESTART}'" -d "${state_dir}"
  1098 
  1099     # We need to do something special with the log file!
  1100     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
  1101         exec >"${state_dir}/tail.log"
  1102     fi
  1103 
  1104     # Log this to the log level required by the user
  1105     CT_DoLog ${CT_LOG_LEVEL_MAX} "Restoring state at step '${state_name}', as requested."
  1106 
  1107     CT_DoExtractTarballIfExists "${state_dir}/prefix_dir" "${CT_PREFIX_DIR}"
  1108     CT_DoExtractTarballIfExists "${state_dir}/cc_core_shared_prefix_dir" "${CT_CC_CORE_SHARED_PREFIX_DIR}"
  1109     CT_DoExtractTarballIfExists "${state_dir}/cc_core_static_prefix_dir" "${CT_CC_CORE_STATIC_PREFIX_DIR}"
  1110     CT_DoExtractTarballIfExists "${state_dir}/config_dir" "${CT_CONFIG_DIR}"
  1111     CT_DoExtractTarballIfExists "${state_dir}/buildtools_dir" "${CT_BUILDTOOLS_PREFIX_DIR}"
  1112 
  1113     # Restore the environment, discarding any error message
  1114     # (for example, read-only bash internals)
  1115     CT_DoLog STATE "  Restoring environment"
  1116     . "${state_dir}/env.sh" >/dev/null 2>&1 || true
  1117 
  1118     # Restore the new RESTART and STOP steps
  1119     CT_RESTART="${old_RESTART}"
  1120     CT_STOP="${old_STOP}"
  1121     unset old_stop old_restart
  1122 
  1123     CT_DoLog STATE "  Restoring log file"
  1124     exec >/dev/null
  1125     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
  1126         y)  zcat "${state_dir}/log.gz" >"${tmp_log_file}";;
  1127         *)  cat "${state_dir}/log" >"${tmp_log_file}";;
  1128     esac
  1129     cat "${state_dir}/tail.log" >>"${tmp_log_file}"
  1130     exec >>"${tmp_log_file}"
  1131     rm -f "${state_dir}/tail.log"
  1132 }