scripts/functions
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Wed Apr 06 22:30:57 2011 +0200 (2011-04-06)
changeset 2381 0ca0f85a4b2a
parent 2340 580f427595bc
child 2382 cbd07f3dd6e3
permissions -rw-r--r--
complibs: disable building shared libs

Managing the shared version of the companion libraries
has become cumbersome.

Also, it will one day be possible to use the companion
libraries from the host distribution, and then we will
be able to easily use either shared or static libs.

As a side note, while working on the canadian-rework
series, it has become quite more complex to properly
handle shared companion libraries, as they need to be
built both for the build and gost systems. That's not
easy to handle. At all.

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