scripts/functions
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Sun Jul 10 00:02:05 2011 +0200 (2011-07-10)
changeset 2543 0e8ff5707383
parent 2501 5f2d85ceb423
child 2574 6e1412ba8da9
permissions -rw-r--r--
scripts: on startup, also remove the buildtools dir

In case there's one lingering around (whether the previous build was
successful, or failed), we have to remove the buildtools directory
as well as the toochain build dir.

This should also fix the case where out makeinfo wrapper calls
itself recursively.

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