scripts/functions
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Wed Sep 21 22:42:35 2011 +0200 (2011-09-21)
changeset 2677 7df89370f805
parent 2660 2a44af825e60
child 2721 7b64746b0ab3
permissions -rw-r--r--
kernel/linux: add alternate download locations

Since kernel.org is dead, and there is no announced or known estimated
time or return to normality, it is impossible to download any kernel at
this time.

Add a known-working mirror.

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