scripts/functions
author Remy Bohmer <linux@bohmer.net>
Sun Jul 11 22:23:34 2010 +0200 (2010-07-11)
changeset 2021 3e52a1510f87
parent 1909 5921089b34bd
child 2029 93f7e40dacdf
permissions -rw-r--r--
debug/gdb: Fix compilation for Mingw hosts

GDB requires PDcurses instead of ncurses while running on Windows.
So, do not always compile ncurses in case GDB needs to build.

PDcurses is provided by an earlier build step and is not described in
this file.

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