scripts/functions
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Fri Aug 19 00:52:05 2011 +0200 (2011-08-19)
changeset 2614 814ea73df7e0
parent 2608 aa09a36c3d36
child 2645 9cb3554bebeb
permissions -rw-r--r--
scripts: simplify and fix the toolchain config script

The script that is installed, and which sole purpose is to dump
the .config that was used to build the toolchain, is pure insanity.

Let's make it much, much more simpler...

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 # Build up the list of allowed tarball extensions
   401 # Add them in the prefered order; most preferred comes first
   402 CT_DoListTarballExt() {
   403     if [ "${CT_CONFIGURE_has_xzutils}" = "y" ]; then
   404         printf ".tar.xz\n"
   405     fi
   406     printf ".tar.bz2\n"
   407     printf ".tar.gz\n.tgz\n"
   408     printf ".tar\n"
   409 }
   410 
   411 # Get the file name extension of a component
   412 # Usage: CT_GetFileExtension <component_name-component_version> [extension]
   413 # If found, echoes the extension to stdout, and return 0
   414 # If not found, echoes nothing on stdout, and return !0.
   415 CT_GetFileExtension() {
   416     local ext
   417     local file="$1"
   418     shift
   419     local first_ext="$1"
   420 
   421     # we need to also check for an empty extension for those very
   422     # peculiar components that don't have one (such as sstrip from
   423     # buildroot).
   424     for ext in ${first_ext} $(CT_DoListTarballExt) /.git ''; do
   425         if [ -e "${CT_TARBALLS_DIR}/${file}${ext}" ]; then
   426             echo "${ext}"
   427             exit 0
   428         fi
   429     done
   430 
   431     exit 1
   432 }
   433 
   434 # Try to retrieve the specified URL (HTTP or FTP)
   435 # Usage: CT_DoGetFile <URL>
   436 # This functions always returns true (0), as it can be legitimate not
   437 # to find the requested URL (think about snapshots, different layouts
   438 # for different gcc versions, etc...).
   439 CT_DoGetFile() {
   440     local url="${1}"
   441     local dest="${CT_TARBALLS_DIR}/${url##*/}"
   442     local tmp="${dest}.tmp-dl"
   443     # OK, just look if we have them...
   444     # We are sure at least one is available, ./configure checked for it.
   445     local _curl=$(CT_Which curl)
   446     local _wget=$(CT_Which wget)
   447     _curl="${_curl:-false}"
   448     _wget="${_wget:-false}"
   449 
   450     # Remove potential left-over from a previous run
   451     rm -f "${tmp}"
   452 
   453     # Some (very old!) FTP server might not support the passive mode, thus
   454     # retry without.
   455     # We also retry a few times, in case there is a transient error (eg. behind
   456     # a dynamic IP that changes during the transfer...)
   457     # With automated download as we are doing, it can be very dangerous to
   458     # continue the downloads. It's far better to simply overwrite the
   459     # destination file.
   460     # Some company networks have firewalls to connect to the internet, but it's
   461     # not easy to detect them, and wget does not timeout by default while
   462     # connecting, so force a global ${CT_CONNECT_TIMEOUT}-second timeout.
   463     # For curl, no good progress indicator is available. So, be silent.
   464     if CT_DoExecLog ALL "${_curl}" --ftp-pasv    --retry 3 --connect-timeout ${CT_CONNECT_TIMEOUT} -L -f -s -o "${tmp}"   "${url}"  \
   465     || CT_DoExecLog ALL "${_curl}"               --retry 3 --connect-timeout ${CT_CONNECT_TIMEOUT} -L -f -s -o "${tmp}"   "${url}"  \
   466     || CT_DoExecLog ALL "${_wget}" --passive-ftp --tries=3 -T ${CT_CONNECT_TIMEOUT} -nc --progress=dot:binary -O "${tmp}" "${url}"  \
   467     || CT_DoExecLog ALL "${_wget}"               --tries=3 -T ${CT_CONNECT_TIMEOUT} -nc --progress=dot:binary -O "${tmp}" "${url}"  \
   468     ; then
   469         # One of them succeeded, good!
   470         mv "${tmp}" "${dest}"
   471     else
   472         # Woops...
   473         rm -f "${tmp}"
   474     fi
   475 }
   476 
   477 # This function tries to retrieve a tarball form a local directory
   478 # Usage: CT_GetLocal <basename> [.extension]
   479 CT_GetLocal() {
   480     local basename="$1"
   481     local first_ext="$2"
   482     local ext
   483 
   484     # Do we already have it in *our* tarballs dir?
   485     if ext="$( CT_GetFileExtension "${basename}" ${first_ext} )"; then
   486         CT_DoLog DEBUG "Already have '${basename}'"
   487         return 0
   488     fi
   489 
   490     if [ -n "${CT_LOCAL_TARBALLS_DIR}" ]; then
   491         CT_DoLog DEBUG "Trying to retrieve an already downloaded copy of '${basename}'"
   492         # We'd rather have a bzip2'ed tarball, then gzipped tarball, plain tarball,
   493         # or, as a failover, a file without extension.
   494         for ext in ${first_ext} $(CT_DoListTarballExt) ''; do
   495             CT_DoLog DEBUG "Trying '${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}'"
   496             if [ -r "${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}" -a \
   497                  "${CT_FORCE_DOWNLOAD}" != "y" ]; then
   498                 CT_DoLog DEBUG "Got '${basename}' from local storage"
   499                 CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}" "${CT_TARBALLS_DIR}/${basename}${ext}"
   500                 return 0
   501             fi
   502         done
   503     fi
   504     return 1
   505 }
   506 
   507 # This function saves the specified to local storage if possible,
   508 # and if so, symlinks it for later usage
   509 # Usage: CT_SaveLocal </full/path/file.name>
   510 CT_SaveLocal() {
   511     local file="$1"
   512     local basename="${file##*/}"
   513 
   514     if [ "${CT_SAVE_TARBALLS}" = "y" ]; then
   515         CT_DoLog EXTRA "Saving '${basename}' to local storage"
   516         # The file may already exist if downloads are forced: remove it first
   517         CT_DoExecLog ALL rm -f "${CT_LOCAL_TARBALLS_DIR}/${basename}"
   518         CT_DoExecLog ALL mv -f "${file}" "${CT_LOCAL_TARBALLS_DIR}"
   519         CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${basename}" "${file}"
   520     fi
   521 }
   522 
   523 # Download the file from one of the URLs passed as argument
   524 # Usage: CT_GetFile <basename> [.extension] <url> [url ...]
   525 CT_GetFile() {
   526     local ext
   527     local -a URLS
   528     local url
   529     local file="$1"
   530     local first_ext
   531     shift
   532     # If next argument starts with a dot, then this is not an URL,
   533     # and we can consider that it is a preferred extension.
   534     case "$1" in
   535         .*) first_ext="$1"
   536             shift
   537             ;;
   538     esac
   539 
   540     # Does it exist localy?
   541     if CT_GetLocal "${file}" ${first_ext}; then
   542         return 0
   543     fi
   544     # No, it does not...
   545 
   546     # Try to retrieve the file
   547     CT_DoLog EXTRA "Retrieving '${file}'"
   548 
   549     # Add URLs on the LAN mirror
   550     if [ "${CT_USE_MIRROR}" = "y" ]; then
   551         CT_TestOrAbort "Please set the mirror base URL" -n "${CT_MIRROR_BASE_URL}"
   552         URLS+=( "${CT_MIRROR_BASE_URL}/${file%-*}" )
   553         URLS+=( "${CT_MIRROR_BASE_URL}" )
   554     fi
   555 
   556     if [ "${CT_FORBID_DOWNLOAD}" != "y" ]; then
   557         URLS+=( "${@}" )
   558     fi
   559 
   560     # Scan all URLs in turn, and try to grab a tarball from there
   561     # Do *not* try git trees (ext=/.git), this is handled in a specific
   562     # wrapper, below
   563     for ext in ${first_ext} $(CT_DoListTarballExt) ''; do
   564         # Try all urls in turn
   565         for url in "${URLS[@]}"; do
   566             [ -n "${url}" ] || continue
   567             CT_DoLog DEBUG "Trying '${url}/${file}${ext}'"
   568             CT_DoGetFile "${url}/${file}${ext}"
   569             if [ -f "${CT_TARBALLS_DIR}/${file}${ext}" ]; then
   570                 CT_DoLog DEBUG "Got '${file}' from the Internet"
   571                 CT_SaveLocal "${CT_TARBALLS_DIR}/${file}${ext}"
   572                 return 0
   573             fi
   574         done
   575     done
   576 
   577     # Just return error, someone may want to catch and handle the error
   578     # (eg. glibc/eglibc add-ons can be missing).
   579     return 1
   580 }
   581 
   582 # Checkout from CVS, and build the associated tarball
   583 # The tarball will be called ${basename}.tar.bz2
   584 # Prerequisite: either the server does not require password,
   585 # or the user must already be logged in.
   586 # 'tag' is the tag to retrieve. Must be specified, but can be empty.
   587 # If dirname is specified, then module will be renamed to dirname
   588 # prior to building the tarball.
   589 # Usage: CT_GetCVS <basename> <url> <module> <tag> [dirname[=subdir]]
   590 # Note: if '=subdir' is given, then it is used instead of 'module'.
   591 CT_GetCVS() {
   592     local basename="$1"
   593     local uri="$2"
   594     local module="$3"
   595     local tag="${4:+-r ${4}}"
   596     local dirname="$5"
   597     local tmp_dir
   598 
   599     # First try locally, then the mirror
   600     if CT_GetFile "${basename}"; then
   601         # Got it! Return early! :-)
   602         return 0
   603     fi
   604 
   605     if [ "${CT_FORBID_DOWNLOAD}" = "y" ]; then
   606         CT_DoLog WARN "Downloads forbidden, not trying cvs retrieval"
   607         return 1
   608     fi
   609 
   610     CT_MktempDir tmp_dir
   611     CT_Pushd "${tmp_dir}"
   612 
   613     CT_DoExecLog ALL cvs -z 9 -d "${uri}" co -P ${tag} "${module}"
   614     if [ -n "${dirname}" ]; then
   615         case "${dirname}" in
   616             *=*)
   617                 CT_DoExecLog DEBUG mv "${dirname#*=}" "${dirname%%=*}"
   618                 CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${dirname%%=*}"
   619                 ;;
   620             *)
   621                 CT_DoExecLog ALL mv "${module}" "${dirname}"
   622                 CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${dirname:-${module}}"
   623                 ;;
   624         esac
   625     fi
   626     CT_SaveLocal "${CT_TARBALLS_DIR}/${basename}.tar.bz2"
   627 
   628     CT_Popd
   629     CT_DoExecLog ALL rm -rf "${tmp_dir}"
   630 }
   631 
   632 # Check out from SVN, and build the associated tarball
   633 # The tarball will be called ${basename}.tar.bz2
   634 # Prerequisite: either the server does not require password,
   635 # or the user must already be logged in.
   636 # 'rev' is the revision to retrieve
   637 # Usage: CT_GetSVN <basename> <url> [rev]
   638 CT_GetSVN() {
   639     local basename="$1"
   640     local uri="$2"
   641     local rev="$3"
   642 
   643     # First try locally, then the mirror
   644     if CT_GetFile "${basename}"; then
   645         # Got it! Return early! :-)
   646         return 0
   647     fi
   648 
   649     if [ "${CT_FORBID_DOWNLOAD}" = "y" ]; then
   650         CT_DoLog WARN "Downloads forbidden, not trying svn retrieval"
   651         return 1
   652     fi
   653 
   654     CT_MktempDir tmp_dir
   655     CT_Pushd "${tmp_dir}"
   656 
   657     if ! CT_DoExecLog ALL svn export ${rev:+-r ${rev}} "${uri}" "${basename}"; then
   658         CT_DoLog WARN "Could not retrieve '${basename}'"
   659         return 1
   660     fi
   661     CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${basename}"
   662     CT_SaveLocal "${CT_TARBALLS_DIR}/${basename}.tar.bz2"
   663 
   664     CT_Popd
   665     CT_DoExecLog ALL rm -rf "${tmp_dir}"
   666 }
   667 
   668 # Clone a git tree
   669 # Tries the given URLs in turn until one can get cloned. No tarball will be created.
   670 # Prerequisites: either the server does not require password,
   671 # or the user has already taken any action to authenticate to the server.
   672 # The cloned tree will *not* be stored in the local tarballs dir!
   673 # Usage: CT_GetGit <basename> <url [url ...]>
   674 CT_GetGit() {
   675     local basename="$1"; shift
   676     local url
   677     local cloned=0
   678 
   679     if [ "${CT_FORBID_DOWNLOAD}" = "y" ]; then
   680         CT_DoLog WARN "Downloads forbidden, not trying git retrieval"
   681         return 1
   682     fi
   683 
   684     # Do we have it in our tarballs dir?
   685     if [ -d "${CT_TARBALLS_DIR}/${basename}/.git" ]; then
   686         CT_DoLog EXTRA "Updating git tree '${basename}'"
   687         CT_Pushd "${CT_TARBALLS_DIR}/${basename}"
   688         CT_DoExecLog ALL git pull
   689         CT_Popd
   690     else
   691         CT_DoLog EXTRA "Retrieving git tree '${basename}'"
   692         for url in "${@}"; do
   693             CT_DoLog ALL "Trying to clone from '${url}'"
   694             CT_DoForceRmdir "${CT_TARBALLS_DIR}/${basename}"
   695             if git clone "${url}" "${CT_TARBALLS_DIR}/${basename}" 2>&1 |CT_DoLog ALL; then
   696                 cloned=1
   697                 break
   698             fi
   699         done
   700         CT_TestOrAbort "Could not clone '${basename}'" ${cloned} -ne 0
   701     fi
   702 }
   703 
   704 # Extract a tarball
   705 # Some tarballs need to be extracted in specific places. Eg.: glibc addons
   706 # must be extracted in the glibc directory; uCLibc locales must be extracted
   707 # in the extra/locale sub-directory of uClibc. This is taken into account
   708 # by the caller, that did a 'cd' into the correct path before calling us
   709 # and sets nochdir to 'nochdir'.
   710 # Note also that this function handles the git trees!
   711 # Usage: CT_Extract <basename> [nochdir] [options]
   712 # where 'options' are dependent on the source (eg. git branch/tag...)
   713 CT_Extract() {
   714     local nochdir="$1"
   715     local basename
   716     local ext
   717     local -a tar_opts
   718 
   719     if [ "${nochdir}" = "nochdir" ]; then
   720         shift
   721         nochdir="$(pwd)"
   722     else
   723         nochdir="${CT_SRC_DIR}"
   724     fi
   725 
   726     basename="$1"
   727     shift
   728 
   729     if ! ext="$(CT_GetFileExtension "${basename}")"; then
   730         CT_DoLog WARN "'${basename}' not found in '${CT_TARBALLS_DIR}'"
   731         return 1
   732     fi
   733     local full_file="${CT_TARBALLS_DIR}/${basename}${ext}"
   734 
   735     # Check if already extracted
   736     if [ -e "${CT_SRC_DIR}/.${basename}.extracted" ]; then
   737         CT_DoLog DEBUG "Already extracted '${basename}'"
   738         return 0
   739     fi
   740 
   741     # Check if previously partially extracted
   742     if [ -e "${CT_SRC_DIR}/.${basename}.extracting" ]; then
   743         CT_DoLog ERROR "The '${basename}' sources were partially extracted."
   744         CT_DoLog ERROR "Please remove first:"
   745         CT_DoLog ERROR " - the source dir for '${basename}', in '${CT_SRC_DIR}'"
   746         CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${basename}.extracting'"
   747         CT_Abort "I'll stop now to avoid any carnage..."
   748     fi
   749     CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${basename}.extracting"
   750 
   751     CT_Pushd "${nochdir}"
   752 
   753     CT_DoLog EXTRA "Extracting '${basename}'"
   754     CT_DoExecLog FILE mkdir -p "${basename}"
   755     tar_opts=( "--strip-components=1" )
   756     tar_opts+=( "-C" "${basename}" )
   757     tar_opts+=( "-xv" )
   758     case "${ext}" in
   759         .tar.xz)      CT_DoExecLog FILE tar "${tar_opts[@]}" --use-compress-program=xz -f "${full_file}";;
   760         .tar.bz2)     CT_DoExecLog FILE tar "${tar_opts[@]}" -j -f "${full_file}";;
   761         .tar.gz|.tgz) CT_DoExecLog FILE tar "${tar_opts[@]}" -z -f "${full_file}";;
   762         .tar)         CT_DoExecLog FILE tar "${tar_opts[@]}" -f "${full_file}";;
   763         /.git)        CT_ExtractGit "${basename}" "${@}";;
   764         *)            CT_DoLog WARN "Don't know how to handle '${basename}${ext}': unknown extension"
   765                       return 1
   766                       ;;
   767     esac
   768 
   769     # Don't mark as being extracted for git
   770     case "${ext}" in
   771         /.git)  ;;
   772         *)      CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${basename}.extracted";;
   773     esac
   774     CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/.${basename}.extracting"
   775 
   776     CT_Popd
   777 }
   778 
   779 # Create a working git clone
   780 # Usage: CT_ExtractGit <basename> [ref]
   781 # where 'ref' is the reference to use:
   782 #   the full name of a branch, like "remotes/origin/branch_name"
   783 #   a date as understandable by git, like "YYYY-MM-DD[ hh[:mm[:ss]]]"
   784 #   a tag name
   785 CT_ExtractGit() {
   786     local basename="${1}"
   787     local ref="${2}"
   788     local clone_dir
   789     local ref_type
   790 
   791     # pushd now to be able to get git revlist in case ref is a date
   792     clone_dir="${CT_TARBALLS_DIR}/${basename}"
   793     CT_Pushd "${clone_dir}"
   794 
   795     # What kind of reference is ${ref} ?
   796     if [ -z "${ref}" ]; then
   797         # Don't update the clone, keep as-is
   798         ref_type=none
   799     elif git tag |grep -E "^${ref}$" >/dev/null 2>&1; then
   800         ref_type=tag
   801     elif git branch -a --no-color |grep -E "^. ${ref}$" >/dev/null 2>&1; then
   802         ref_type=branch
   803     elif date -d "${ref}" >/dev/null 2>&1; then
   804         ref_type=date
   805         ref=$(git rev-list -n1 --before="${ref}")
   806     else
   807         CT_Abort "Reference '${ref}' is an incorrect git reference: neither tag, branch nor date"
   808     fi
   809 
   810     CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/${basename}"
   811     CT_DoExecLog ALL ln -sf "${clone_dir}" "${CT_SRC_DIR}/${basename}"
   812 
   813     case "${ref_type}" in
   814         none)   ;;
   815         *)      CT_DoExecLog FILE git checkout "${ref}";;
   816     esac
   817 
   818     CT_Popd
   819 }
   820 
   821 # Patches the specified component
   822 # See CT_Extract, above, for explanations on 'nochdir'
   823 # Usage: CT_Patch [nochdir] <packagename> <packageversion>
   824 # If the package directory is *not* packagename-packageversion, then
   825 # the caller must cd into the proper directory first, and call us
   826 # with nochdir
   827 CT_Patch() {
   828     local nochdir="$1"
   829     local pkgname
   830     local version
   831     local pkgdir
   832     local base_file
   833     local ver_file
   834     local d
   835     local -a patch_dirs
   836     local bundled_patch_dir
   837     local local_patch_dir
   838 
   839     if [ "${nochdir}" = "nochdir" ]; then
   840         shift
   841         pkgname="$1"
   842         version="$2"
   843         pkgdir="${pkgname}-${version}"
   844         nochdir="$(pwd)"
   845     else
   846         pkgname="$1"
   847         version="$2"
   848         pkgdir="${pkgname}-${version}"
   849         nochdir="${CT_SRC_DIR}/${pkgdir}"
   850     fi
   851 
   852     # Check if already patched
   853     if [ -e "${CT_SRC_DIR}/.${pkgdir}.patched" ]; then
   854         CT_DoLog DEBUG "Already patched '${pkgdir}'"
   855         return 0
   856     fi
   857 
   858     # Check if already partially patched
   859     if [ -e "${CT_SRC_DIR}/.${pkgdir}.patching" ]; then
   860         CT_DoLog ERROR "The '${pkgdir}' sources were partially patched."
   861         CT_DoLog ERROR "Please remove first:"
   862         CT_DoLog ERROR " - the source dir for '${pkgdir}', in '${CT_SRC_DIR}'"
   863         CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${pkgdir}.extracted'"
   864         CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${pkgdir}.patching'"
   865         CT_Abort "I'll stop now to avoid any carnage..."
   866     fi
   867     touch "${CT_SRC_DIR}/.${pkgdir}.patching"
   868 
   869     CT_Pushd "${nochdir}"
   870 
   871     CT_DoLog EXTRA "Patching '${pkgdir}'"
   872 
   873     bundled_patch_dir="${CT_LIB_DIR}/patches/${pkgname}/${version}"
   874     local_patch_dir="${CT_LOCAL_PATCH_DIR}/${pkgname}/${version}"
   875 
   876     case "${CT_PATCH_ORDER}" in
   877         bundled)        patch_dirs=("${bundled_patch_dir}");;
   878         local)          patch_dirs=("${local_patch_dir}");;
   879         bundled,local)  patch_dirs=("${bundled_patch_dir}" "${local_patch_dir}");;
   880         local,bundled)  patch_dirs=("${local_patch_dir}" "${bundled_patch_dir}");;
   881         none)           patch_dirs=;;
   882     esac
   883 
   884     for d in "${patch_dirs[@]}"; do
   885         CT_DoLog DEBUG "Looking for patches in '${d}'..."
   886         if [ -n "${d}" -a -d "${d}" ]; then
   887             for p in "${d}"/*.patch; do
   888                 if [ -f "${p}" ]; then
   889                     CT_DoLog DEBUG "Applying patch '${p}'"
   890                     CT_DoExecLog ALL patch --no-backup-if-mismatch -g0 -F1 -p1 -f <"${p}"
   891                 fi
   892             done
   893             if [ "${CT_PATCH_SINGLE}" = "y" ]; then
   894                 break
   895             fi
   896         fi
   897     done
   898 
   899     if [ "${CT_OVERIDE_CONFIG_GUESS_SUB}" = "y" ]; then
   900         CT_DoLog ALL "Overiding config.guess and config.sub"
   901         for cfg in config_guess config_sub; do
   902             eval ${cfg}="${CT_LIB_DIR}/scripts/${cfg/_/.}"
   903             [ -e "${CT_TOP_DIR}/scripts/${cfg/_/.}" ] && eval ${cfg}="${CT_TOP_DIR}/scripts/${cfg/_/.}"
   904             # Can't use CT_DoExecLog because of the '{} \;' to be passed un-mangled to find
   905             find . -type f -name "${cfg/_/.}" -exec cp -v "${!cfg}" {} \; |CT_DoLog ALL
   906         done
   907     fi
   908 
   909     CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${pkgdir}.patched"
   910     CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/.${pkgdir}.patching"
   911 
   912     CT_Popd
   913 }
   914 
   915 # Two wrappers to call config.(guess|sub) either from CT_TOP_DIR or CT_LIB_DIR.
   916 # Those from CT_TOP_DIR, if they exist, will be be more recent than those from CT_LIB_DIR.
   917 CT_DoConfigGuess() {
   918     if [ -x "${CT_TOP_DIR}/scripts/config.guess" ]; then
   919         "${CT_TOP_DIR}/scripts/config.guess"
   920     else
   921         "${CT_LIB_DIR}/scripts/config.guess"
   922     fi
   923 }
   924 
   925 CT_DoConfigSub() {
   926     if [ -x "${CT_TOP_DIR}/scripts/config.sub" ]; then
   927         "${CT_TOP_DIR}/scripts/config.sub" "$@"
   928     else
   929         "${CT_LIB_DIR}/scripts/config.sub" "$@"
   930     fi
   931 }
   932 
   933 # Compute the target tuple from what is provided by the user
   934 # Usage: CT_DoBuildTargetTuple
   935 # In fact this function takes the environment variables to build the target
   936 # tuple. It is needed both by the normal build sequence, as well as the
   937 # sample saving sequence.
   938 CT_DoBuildTargetTuple() {
   939     # Set the endianness suffix, and the default endianness gcc option
   940     case "${CT_ARCH_BE},${CT_ARCH_LE}" in
   941         y,) target_endian_eb=eb
   942             target_endian_el=
   943             CT_ARCH_ENDIAN_CFLAG="-mbig-endian"
   944             CT_ARCH_ENDIAN_LDFLAG="-EB"
   945             ;;
   946         ,y) target_endian_eb=
   947             target_endian_el=el
   948             CT_ARCH_ENDIAN_CFLAG="-mlittle-endian"
   949             CT_ARCH_ENDIAN_LDFLAG="-EL"
   950             ;;
   951     esac
   952 
   953     # Build the default architecture tuple part
   954     CT_TARGET_ARCH="${CT_ARCH}"
   955 
   956     # Set defaults for the system part of the tuple. Can be overriden
   957     # by architecture-specific values.
   958     case "${CT_LIBC}" in
   959         *glibc) CT_TARGET_SYS=gnu;;
   960         uClibc) CT_TARGET_SYS=uclibc;;
   961         *)      CT_TARGET_SYS=elf;;
   962     esac
   963 
   964     # Set the default values for ARCH, ABI, CPU, TUNE, FPU and FLOAT
   965     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
   966     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
   967     [ "${CT_ARCH_ARCH}"     ] && { CT_ARCH_ARCH_CFLAG="-march=${CT_ARCH_ARCH}";  CT_ARCH_WITH_ARCH="--with-arch=${CT_ARCH_ARCH}"; }
   968     [ "${CT_ARCH_ABI}"      ] && { CT_ARCH_ABI_CFLAG="-mabi=${CT_ARCH_ABI}";     CT_ARCH_WITH_ABI="--with-abi=${CT_ARCH_ABI}";    }
   969     [ "${CT_ARCH_CPU}"      ] && { CT_ARCH_CPU_CFLAG="-mcpu=${CT_ARCH_CPU}";     CT_ARCH_WITH_CPU="--with-cpu=${CT_ARCH_CPU}";    }
   970     [ "${CT_ARCH_TUNE}"     ] && { CT_ARCH_TUNE_CFLAG="-mtune=${CT_ARCH_TUNE}";  CT_ARCH_WITH_TUNE="--with-tune=${CT_ARCH_TUNE}"; }
   971     [ "${CT_ARCH_FPU}"      ] && { CT_ARCH_FPU_CFLAG="-mfpu=${CT_ARCH_FPU}";     CT_ARCH_WITH_FPU="--with-fpu=${CT_ARCH_FPU}";    }
   972     [ "${CT_ARCH_FLOAT_SW}" ] && { CT_ARCH_FLOAT_CFLAG="-msoft-float";           CT_ARCH_WITH_FLOAT="--with-float=soft";          }
   973 
   974     # Build the default kernel tuple part
   975     CT_TARGET_KERNEL="${CT_KERNEL}"
   976 
   977     # Overide the default values with the components specific settings
   978     CT_DoArchTupleValues
   979     CT_DoKernelTupleValues
   980 
   981     # Finish the target tuple construction
   982     CT_TARGET="${CT_TARGET_ARCH}"
   983     CT_TARGET="${CT_TARGET}${CT_TARGET_VENDOR:+-${CT_TARGET_VENDOR}}"
   984     CT_TARGET="${CT_TARGET}${CT_TARGET_KERNEL:+-${CT_TARGET_KERNEL}}"
   985     CT_TARGET="${CT_TARGET}${CT_TARGET_SYS:+-${CT_TARGET_SYS}}"
   986 
   987     # Sanity checks
   988     __sed_alias=""
   989     if [ -n "${CT_TARGET_ALIAS_SED_EXPR}" ]; then
   990         __sed_alias=$(echo "${CT_TARGET}" |sed -r -e "${CT_TARGET_ALIAS_SED_EXPR}")
   991     fi
   992     case ":${CT_TARGET_VENDOR}:${CT_TARGET_ALIAS}:${__sed_alias}:" in
   993       :*" "*:*:*:) CT_Abort "Don't use spaces in the vendor string, it breaks things.";;
   994       :*"-"*:*:*:) CT_Abort "Don't use dashes in the vendor string, it breaks things.";;
   995       :*:*" "*:*:) CT_Abort "Don't use spaces in the target alias, it breaks things.";;
   996       :*:*:*" "*:) CT_Abort "Don't use spaces in the target sed transform, it breaks things.";;
   997     esac
   998 
   999     # Canonicalise it
  1000     CT_TARGET=$(CT_DoConfigSub "${CT_TARGET}")
  1001     # Prepare the target CFLAGS
  1002     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ENDIAN_CFLAG}"
  1003     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ARCH_CFLAG}"
  1004     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ABI_CFLAG}"
  1005     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_CPU_CFLAG}"
  1006     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_TUNE_CFLAG}"
  1007     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FPU_CFLAG}"
  1008     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FLOAT_CFLAG}"
  1009 
  1010     # Now on for the target LDFLAGS
  1011     CT_ARCH_TARGET_LDFLAGS="${CT_ARCH_TARGET_LDFLAGS} ${CT_ARCH_ENDIAN_LDFLAG}"
  1012 }
  1013 
  1014 # This function does pause the build until the user strikes "Return"
  1015 # Usage: CT_DoPause [optional_message]
  1016 CT_DoPause() {
  1017     local foo
  1018     local message="${1:-Pausing for your pleasure}"
  1019     CT_DoLog INFO "${message}"
  1020     read -p "Press 'Enter' to continue, or Ctrl-C to stop..." foo >&6
  1021     return 0
  1022 }
  1023 
  1024 # This function creates a tarball of the specified directory, but
  1025 # only if it exists
  1026 # Usage: CT_DoTarballIfExists <dir> <tarball_basename> [extra_tar_options [...]]
  1027 CT_DoTarballIfExists() {
  1028     local dir="$1"
  1029     local tarball="$2"
  1030     shift 2
  1031     local -a extra_tar_opts=( "$@" )
  1032     local -a compress
  1033 
  1034     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
  1035         y)  compress=( gzip -c -3 - ); tar_ext=.gz;;
  1036         *)  compress=( cat - );        tar_ext=;;
  1037     esac
  1038 
  1039     if [ -d "${dir}" ]; then
  1040         CT_DoLog DEBUG "  Saving '${dir}'"
  1041         { tar c -C "${dir}" -v -f - "${extra_tar_opts[@]}" .    \
  1042           |"${compress[@]}" >"${tarball}.tar${tar_ext}"         ;
  1043         } 2>&1 |sed -r -e 's/^/    /;' |CT_DoLog STATE
  1044     else
  1045         CT_DoLog STATE "  Not saving '${dir}': does not exist"
  1046     fi
  1047 }
  1048 
  1049 # This function extracts a tarball to the specified directory, but
  1050 # only if the tarball exists
  1051 # Usage: CT_DoExtractTarballIfExists <tarball_basename> <dir> [extra_tar_options [...]]
  1052 CT_DoExtractTarballIfExists() {
  1053     local tarball="$1"
  1054     local dir="$2"
  1055     shift 2
  1056     local -a extra_tar_opts=( "$@" )
  1057     local -a uncompress
  1058 
  1059     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
  1060         y)  uncompress=( gzip -c -d ); tar_ext=.gz;;
  1061         *)  uncompress=( cat );        tar_ext=;;
  1062     esac
  1063 
  1064     if [ -f "${tarball}.tar${tar_ext}" ]; then
  1065         CT_DoLog DEBUG "  Restoring '${dir}'"
  1066         CT_DoForceRmdir "${dir}"
  1067         CT_DoExecLog DEBUG mkdir -p "${dir}"
  1068         { "${uncompress[@]}" "${tarball}.tar${tar_ext}"     \
  1069           |tar x -C "${dir}" -v -f - "${extra_tar_opts[@]}" ;
  1070         } 2>&1 |sed -r -e 's/^/    /;' |CT_DoLog STATE
  1071     else
  1072         CT_DoLog STATE "  Not restoring '${dir}': does not exist"
  1073     fi
  1074 }
  1075 
  1076 # This function saves the state of the toolchain to be able to restart
  1077 # at any one point
  1078 # Usage: CT_DoSaveState <next_step_name>
  1079 CT_DoSaveState() {
  1080 	[ "${CT_DEBUG_CT_SAVE_STEPS}" = "y" ] || return 0
  1081     local state_name="$1"
  1082     local state_dir="${CT_STATE_DIR}/${state_name}"
  1083 
  1084     # Log this to the log level required by the user
  1085     CT_DoLog ${CT_LOG_LEVEL_MAX} "Saving state to restart at step '${state_name}'..."
  1086 
  1087     rm -rf "${state_dir}"
  1088     mkdir -p "${state_dir}"
  1089 
  1090     CT_DoLog STATE "  Saving environment and aliases"
  1091     # We must omit shell functions, and some specific bash variables
  1092     # that break when restoring the environment, later. We could do
  1093     # all the processing in the awk script, but a sed is easier...
  1094     set |awk '
  1095               BEGIN { _p = 1; }
  1096               $0~/^[^ ]+ \(\)/ { _p = 0; }
  1097               _p == 1
  1098               $0 == "}" { _p = 1; }
  1099               ' |sed -r -e '/^BASH_(ARGC|ARGV|LINENO|SOURCE|VERSINFO)=/d;
  1100                            /^(UID|EUID)=/d;
  1101                            /^(FUNCNAME|GROUPS|PPID|SHELLOPTS)=/d;' >"${state_dir}/env.sh"
  1102 
  1103     CT_DoTarballIfExists "${CT_BUILDTOOLS_PREFIX_DIR}" "${state_dir}/buildtools_dir"
  1104     CT_DoTarballIfExists "${CT_COMPLIBS_DIR}" "${state_dir}/complibs_dir"
  1105     CT_DoTarballIfExists "${CT_CONFIG_DIR}" "${state_dir}/config_dir"
  1106     CT_DoTarballIfExists "${CT_CC_CORE_STATIC_PREFIX_DIR}" "${state_dir}/cc_core_static_prefix_dir"
  1107     CT_DoTarballIfExists "${CT_CC_CORE_SHARED_PREFIX_DIR}" "${state_dir}/cc_core_shared_prefix_dir"
  1108     CT_DoTarballIfExists "${CT_PREFIX_DIR}" "${state_dir}/prefix_dir" --exclude '*.log'
  1109 
  1110     CT_DoLog STATE "  Saving log file"
  1111     exec >/dev/null
  1112     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
  1113         y)  gzip -3 -c "${tmp_log_file}"  >"${state_dir}/log.gz";;
  1114         *)  cat "${tmp_log_file}" >"${state_dir}/log";;
  1115     esac
  1116     exec >>"${tmp_log_file}"
  1117 }
  1118 
  1119 # This function restores a previously saved state
  1120 # Usage: CT_DoLoadState <state_name>
  1121 CT_DoLoadState(){
  1122     local state_name="$1"
  1123     local state_dir="${CT_STATE_DIR}/${state_name}"
  1124     local old_RESTART="${CT_RESTART}"
  1125     local old_STOP="${CT_STOP}"
  1126 
  1127     CT_TestOrAbort "The previous build did not reach the point where it could be restarted at '${CT_RESTART}'" -d "${state_dir}"
  1128 
  1129     # We need to do something special with the log file!
  1130     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
  1131         exec >"${state_dir}/tail.log"
  1132     fi
  1133 
  1134     # Log this to the log level required by the user
  1135     CT_DoLog ${CT_LOG_LEVEL_MAX} "Restoring state at step '${state_name}', as requested."
  1136 
  1137     CT_DoExtractTarballIfExists "${state_dir}/prefix_dir" "${CT_PREFIX_DIR}"
  1138     CT_DoExtractTarballIfExists "${state_dir}/cc_core_shared_prefix_dir" "${CT_CC_CORE_SHARED_PREFIX_DIR}"
  1139     CT_DoExtractTarballIfExists "${state_dir}/cc_core_static_prefix_dir" "${CT_CC_CORE_STATIC_PREFIX_DIR}"
  1140     CT_DoExtractTarballIfExists "${state_dir}/config_dir" "${CT_CONFIG_DIR}"
  1141     CT_DoExtractTarballIfExists "${state_dir}/complibs_dir" "${CT_COMPLIBS_DIR}"
  1142     CT_DoExtractTarballIfExists "${state_dir}/buildtools_dir" "${CT_BUILDTOOLS_PREFIX_DIR}"
  1143 
  1144     # Restore the environment, discarding any error message
  1145     # (for example, read-only bash internals)
  1146     CT_DoLog STATE "  Restoring environment"
  1147     . "${state_dir}/env.sh" >/dev/null 2>&1 || true
  1148 
  1149     # Restore the new RESTART and STOP steps
  1150     CT_RESTART="${old_RESTART}"
  1151     CT_STOP="${old_STOP}"
  1152     unset old_stop old_restart
  1153 
  1154     CT_DoLog STATE "  Restoring log file"
  1155     exec >/dev/null
  1156     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
  1157         y)  zcat "${state_dir}/log.gz" >"${tmp_log_file}";;
  1158         *)  cat "${state_dir}/log" >"${tmp_log_file}";;
  1159     esac
  1160     cat "${state_dir}/tail.log" >>"${tmp_log_file}"
  1161     exec >>"${tmp_log_file}"
  1162     rm -f "${state_dir}/tail.log"
  1163 }