scripts/functions
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Mon Jan 03 23:40:22 2011 +0100 (2011-01-03)
changeset 2267 7af68e6083aa
parent 2204 ea1c9143e1e3
child 2280 3d480d12124e
permissions -rw-r--r--
libc-glibc: remove 2.3.6

This is an obsolete version which is no longer used by any sample (the only
user, the ia64 sample, has been removed).

It also makes the code path a bit complex, with twists just to accomodate
that version. Removing the version will make those twists go away, and
will ease commonalisation of glibc and eglibc in the future (hopefully!).

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