scripts/functions
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Thu Aug 25 18:30:18 2011 +0200 (2011-08-25)
changeset 2645 9cb3554bebeb
parent 2609 409fe6b9ede2
child 2646 e5078db4bd2c
permissions -rw-r--r--
configure: check for lzma

Some packages are available as LZMA tarballs. LZMA is a relatively recent
compression algorithm; it's slightly better than bzip2, but offers much
faster decompression. LZMA is now deprecated in favor of XZ, but some
packages switched to LZMA when XZ was not yet available, or still in its
infancy. Latest XZ (which totaly obsoletes LZMA) offers a backward LZMA-
compatible utility, so we can check for 'lzma' nonetheless.

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