scripts/functions
author Remy Bohmer <linux@bohmer.net>
Thu May 27 23:18:19 2010 +0200 (2010-05-27)
changeset 2060 51e4597b07fc
parent 2044 20dd8cef1c8a
child 2079 d2ceb140371d
child 2092 18da1aa1beab
permissions -rw-r--r--
scripts: add option to strip all toolchain executables

To reduce filesizes of the toolchain and even improve build times
of projects to be build with this toolchain it is usefull to strip
the delivered toolchain executables. Since it is not likely that we
will debug the toolchain executables itself we do not need the
debug information inside the executables itself.

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