scripts/functions
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Tue May 22 20:46:07 2007 +0000 (2007-05-22)
changeset 121 82e69d88119b
parent 112 ea15433daba0
child 127 2ba6a8d5d4be
permissions -rw-r--r--
Implement a restart facility.
If you select to debug ct-ng, then you have two new options:
- DEBUG_CT_PAUSE_STEPS : pause between every steps,
- DEBUG_CT_SAVE_STEPS : save state between every steps.
To restart a saved state, just set the RESTART make variable when calling make:
- make RESTART=<step_name>
     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 CT_OnError() {
     6     ret=$?
     7     CT_DoLog ERROR "Build failed in step \"${CT_STEP_MESSAGE[${CT_STEP_COUNT}]}\""
     8     for((step=(CT_STEP_COUNT-1); step>1; step--)); do
     9         CT_DoLog ERROR "      called in step \"${CT_STEP_MESSAGE[${step}]}\""
    10     done
    11     CT_DoLog ERROR "Error happened in \"${BASH_SOURCE[1]}\" in function \"${FUNCNAME[1]}\" (line unknown, sorry)"
    12     for((depth=2; ${BASH_LINENO[$((${depth}-1))]}>0; depth++)); do
    13         CT_DoLog ERROR "      called from \"${BASH_SOURCE[${depth}]}\" at line # ${BASH_LINENO[${depth}-1]} in function \"${FUNCNAME[${depth}]}\""
    14     done
    15     [ "${CT_LOG_TO_FILE}" = "y" ] && CT_DoLog ERROR "Look at \"${CT_LOG_FILE}\" for more info on this error."
    16     CT_STEP_COUNT=1
    17     CT_DoEnd ERROR
    18     exit $ret
    19 }
    20 trap CT_OnError ERR
    21 
    22 set -E
    23 set -o pipefail
    24 
    25 # The different log levels:
    26 CT_LOG_LEVEL_ERROR=0
    27 CT_LOG_LEVEL_WARN=1
    28 CT_LOG_LEVEL_INFO=2
    29 CT_LOG_LEVEL_EXTRA=3
    30 CT_LOG_LEVEL_DEBUG=4
    31 CT_LOG_LEVEL_ALL=5
    32 
    33 # A function to log what is happening
    34 # Different log level are available:
    35 #   - ERROR:   A serious, fatal error occurred
    36 #   - WARN:    A non fatal, non serious error occurred, take your responsbility with the generated build
    37 #   - INFO:    Informational messages
    38 #   - EXTRA:   Extra informational messages
    39 #   - DEBUG:   Debug messages
    40 #   - ALL:     Component's build messages
    41 # Usage: CT_DoLog <level> [message]
    42 # If message is empty, then stdin will be logged.
    43 CT_DoLog() {
    44     local max_level LEVEL level cur_l cur_L
    45     local l
    46     eval max_level="\${CT_LOG_LEVEL_${CT_LOG_LEVEL_MAX}}"
    47     # Set the maximum log level to DEBUG if we have none
    48     [ -z "${max_level}" ] && max_level=${CT_LOG_LEVEL_DEBUG}
    49 
    50     LEVEL="$1"; shift
    51     eval level="\${CT_LOG_LEVEL_${LEVEL}}"
    52 
    53     if [ $# -eq 0 ]; then
    54         cat -
    55     else
    56         echo "${1}"
    57     fi |( offset=$((`CT_DoDate +%s`+(CT_STAR_DATE/(1000*1000*1000))))
    58           IFS="\n" # We want the full lines, even leading spaces
    59           CT_PROG_BAR_CPT=0
    60           indent=$((2*CT_STEP_COUNT))
    61           while read line; do
    62               case "${CT_LOG_SEE_TOOLS_WARN},${line}" in
    63                 y,*"warning:"*)         cur_L=WARN; cur_l=${CT_LOG_LEVEL_WARN};;
    64                 y,*"WARNING:"*)         cur_L=WARN; cur_l=${CT_LOG_LEVEL_WARN};;
    65                 *"error:"*)             cur_L=ERROR; cur_l=${CT_LOG_LEVEL_ERROR};;
    66                 *"make["?*"]:"*"Stop.") cur_L=ERROR; cur_l=${CT_LOG_LEVEL_ERROR};;
    67                 *)                      cur_L="${LEVEL}"; cur_l="${level}";;
    68               esac
    69               l="`printf \"[%-5s]%*s%s%s\" \"${cur_L}\" \"${indent}\" \" \" \"${line}\"`"
    70               # There will always be a log file, be it /dev/null
    71               echo -e "${l}"
    72               if [ ${cur_l} -le ${max_level} ]; then
    73                   echo -e "\r${l}" >&6
    74               fi
    75               if [ "${CT_LOG_PROGRESS_BAR}" = "y" ]; then
    76                   elapsed=$((SECONDS+OFFSET))
    77                   [ ${CT_PROG_BAR_CPT} -eq 0  ] && bar="/"
    78                   [ ${CT_PROG_BAR_CPT} -eq 10 ] && bar="-"
    79                   [ ${CT_PROG_BAR_CPT} -eq 20 ] && bar="\\"
    80                   [ ${CT_PROG_BAR_CPT} -eq 30 ] && bar="|"
    81                   printf "\r[%02d:%02d] %s " $((elapsed/60)) $((elapsed%60)) "${bar}" >&6
    82                   CT_PROG_BAR_CPT=$(((CT_PROG_BAR_CPT+1)%40))
    83               fi
    84           done
    85         )
    86 
    87     return 0
    88 }
    89 
    90 # Tail message to be logged whatever happens
    91 # Usage: CT_DoEnd <level>
    92 CT_DoEnd()
    93 {
    94     CT_STOP_DATE=`CT_DoDate +%s%N`
    95     CT_STOP_DATE_HUMAN=`CT_DoDate +%Y%m%d.%H%M%S`
    96     CT_DoLog INFO "Build completed at ${CT_STOP_DATE_HUMAN}"
    97     elapsed=$((CT_STOP_DATE-CT_STAR_DATE))
    98     elapsed_min=$((elapsed/(60*1000*1000*1000)))
    99     elapsed_sec=`printf "%02d" $(((elapsed%(60*1000*1000*1000))/(1000*1000*1000)))`
   100     elapsed_csec=`printf "%02d" $(((elapsed%(1000*1000*1000))/(10*1000*1000)))`
   101     CT_DoLog ${1:-INFO} "(elapsed: ${elapsed_min}:${elapsed_sec}.${elapsed_csec})"
   102 }
   103 
   104 # Abort the execution with an error message
   105 # Usage: CT_Abort <message>
   106 CT_Abort() {
   107     CT_DoLog ERROR "$1" >&2
   108     exit 1
   109 }
   110 
   111 # Test a condition, and print a message if satisfied
   112 # Usage: CT_Test <message> <tests>
   113 CT_Test() {
   114     local ret
   115     local m="$1"
   116     shift
   117     test "$@" && CT_DoLog WARN "$m"
   118     return 0
   119 }
   120 
   121 # Test a condition, and abort with an error message if satisfied
   122 # Usage: CT_TestAndAbort <message> <tests>
   123 CT_TestAndAbort() {
   124     local m="$1"
   125     shift
   126     test "$@" && CT_Abort "$m"
   127     return 0
   128 }
   129 
   130 # Test a condition, and abort with an error message if not satisfied
   131 # Usage: CT_TestAndAbort <message> <tests>
   132 CT_TestOrAbort() {
   133     local m="$1"
   134     shift
   135     test "$@" || CT_Abort "$m"
   136     return 0
   137 }
   138 
   139 # Test the presence of a tool, or abort if not found
   140 # Usage: CT_HasOrAbort <tool>
   141 CT_HasOrAbort() {
   142     CT_TestAndAbort "\"${1}\" not found and needed for successfull toolchain build." -z "`which \"${1}\"`"
   143     return 0
   144 }
   145 
   146 # Get current date with nanosecond precision
   147 # On those system not supporting nanosecond precision, faked with rounding down
   148 # to the highest entire second
   149 # Usage: CT_DoDate <fmt>
   150 CT_DoDate() {
   151     date "$1" |sed -r -e 's/%N$/000000000/;'
   152 }
   153 
   154 CT_STEP_COUNT=1
   155 CT_STEP_MESSAGE[${CT_STEP_COUNT}]="<none>"
   156 # Memorise a step being done so that any error is caught
   157 # Usage: CT_DoStep <loglevel> <message>
   158 CT_DoStep() {
   159     local start=`CT_DoDate +%s%N`
   160     CT_DoLog "$1" "================================================================="
   161     CT_DoLog "$1" "$2"
   162     CT_STEP_COUNT=$((CT_STEP_COUNT+1))
   163     CT_STEP_LEVEL[${CT_STEP_COUNT}]="$1"; shift
   164     CT_STEP_START[${CT_STEP_COUNT}]="${start}"
   165     CT_STEP_MESSAGE[${CT_STEP_COUNT}]="$1"
   166     return 0
   167 }
   168 
   169 # End the step just being done
   170 # Usage: CT_EndStep
   171 CT_EndStep() {
   172     local stop=`CT_DoDate +%s%N`
   173     local duration=`printf "%032d" $((stop-${CT_STEP_START[${CT_STEP_COUNT}]})) |sed -r -e 's/([[:digit:]]{2})[[:digit:]]{7}$/\.\1/; s/^0+//; s/^\./0\./;'`
   174     local level="${CT_STEP_LEVEL[${CT_STEP_COUNT}]}"
   175     local message="${CT_STEP_MESSAGE[${CT_STEP_COUNT}]}"
   176     CT_STEP_COUNT=$((CT_STEP_COUNT-1))
   177     CT_DoLog "${level}" "${message}: done in ${duration}s"
   178     return 0
   179 }
   180 
   181 # Pushes into a directory, and pops back
   182 CT_Pushd() {
   183     pushd "$1" >/dev/null 2>&1
   184 }
   185 CT_Popd() {
   186     popd >/dev/null 2>&1
   187 }
   188 
   189 # Makes a path absolute
   190 # Usage: CT_MakeAbsolutePath path
   191 CT_MakeAbsolutePath() {
   192     # Try to cd in that directory
   193     if [ -d "$1" ]; then
   194         CT_Pushd "$1"
   195         pwd
   196         CT_Popd
   197     else
   198         # No such directory, fail back to guessing
   199         case "$1" in
   200             /*)  echo "$1";;
   201             *)   echo "`pwd`/$1";;
   202         esac
   203     fi
   204     
   205     return 0
   206 }
   207 
   208 # Creates a temporary directory
   209 # $1: variable to assign to
   210 # Usage: CT_MktempDir foo
   211 CT_MktempDir() {
   212     # Some mktemp do not allow more than 6 Xs
   213     eval "$1"="`mktemp -q -d \"${CT_BUILD_DIR}/.XXXXXX\"`"
   214     CT_TestOrAbort "Could not make temporary directory" -n "${!1}" -a -d "${!1}"
   215 }
   216 
   217 # Echoes the specified string on stdout until the pipe breaks.
   218 # Doesn't fail
   219 # $1: string to echo
   220 # Usage: CT_DoYes "" |make oldconfig
   221 CT_DoYes() {
   222     yes "$1" || true
   223 }
   224 
   225 # Get the file name extension of a component
   226 # Usage: CT_GetFileExtension <component_name-component_version>
   227 # If found, echoes the extension to stdout
   228 # If not found, echoes nothing on stdout.
   229 CT_GetFileExtension() {
   230     local ext
   231     local file="$1"
   232     local got_it=1
   233 
   234     CT_Pushd "${CT_TARBALLS_DIR}"
   235     for ext in .tar.gz .tar.bz2 .tgz .tar; do
   236         if [ -f "${file}${ext}" ]; then
   237             echo "${ext}"
   238             got_it=0
   239             break
   240         fi
   241     done
   242     CT_Popd
   243 
   244     return 0
   245 }
   246 
   247 # Download an URL using wget
   248 # Usage: CT_DoGetFileWget <URL>
   249 CT_DoGetFileWget() {
   250     # Need to return true because it is legitimate to not find the tarball at
   251     # some of the provided URLs (think about snapshots, different layouts for
   252     # different gcc versions, etc...)
   253     # Some (very old!) FTP server might not support the passive mode, thus
   254     # retry without
   255     # With automated download as we are doing, it can be very dangerous to use
   256     # -c to continue the downloads. It's far better to simply overwrite the
   257     # destination file
   258     wget -nc --progress=dot:binary --tries=3 --passive-ftp "$1" || wget -nc --progress=dot:binary --tries=3 "$1" || true
   259 }
   260 
   261 # Download an URL using curl
   262 # Usage: CT_DoGetFileCurl <URL>
   263 CT_DoGetFileCurl() {
   264 	# Note: comments about wget method are also valid here
   265 	# Plus: no good progreess indicator is available with curl,
   266 	#       so output is consigned to oblivion
   267 	curl --ftp-pasv -O --retry 3 "$1" >/dev/null || curl -O --retry 3 "$1" >/dev/null || true
   268 }
   269 
   270 # Wrapper function to call one of curl or wget
   271 # Usage: CT_DoGetFile <URL>
   272 CT_DoGetFile() {
   273     local _wget=`which wget`
   274     local _curl=`which curl`
   275     case "${_wget},${_curl}" in
   276         ,)  CT_DoError "Could find neither wget nor curl";;
   277         ,*) CT_DoGetFileCurl "$1" 2>&1 |CT_DoLog DEBUG;;
   278         *)  CT_DoGetFileWget "$1" 2>&1 |CT_DoLog DEBUG;;
   279     esac
   280 }
   281 
   282 # Download the file from one of the URLs passed as argument
   283 # Usage: CT_GetFile <filename> <url> [<url> ...]
   284 CT_GetFile() {
   285     local got_it
   286     local ext
   287     local url
   288     local file="$1"
   289     shift
   290 
   291     # Do we already have it?
   292     ext=`CT_GetFileExtension "${file}"`
   293     if [ -n "${ext}" ]; then
   294         CT_DoLog DEBUG "Already have \"${file}\""
   295         return 0
   296     fi
   297 
   298     CT_Pushd "${CT_TARBALLS_DIR}"
   299     # File not yet downloaded, try to get it
   300     got_it=0
   301     # We'd rather have a bzip2'ed tarball, then gzipped, and finally plain tar.
   302     # Try local copy first, if it exists
   303     for ext in .tar.bz2 .tar.gz .tgz .tar; do
   304         if [ -r "${CT_LOCAL_TARBALLS_DIR}/${file}${ext}" -a \
   305              "${CT_FORCE_DOWNLOAD}" != "y" ]; then
   306             CT_DoLog EXTRA "Copying \"${file}\" from local copy"
   307             cp -v "${CT_LOCAL_TARBALLS_DIR}/${file}${ext}" "${file}${ext}" |CT_DoLog ALL
   308             return 0
   309         fi
   310     done
   311     # Try to download it
   312     CT_DoLog EXTRA "Retrieving \"${file}\""
   313     for ext in .tar.bz2 .tar.gz .tgz .tar; do
   314         # Try all urls in turn
   315         for url in "$@"; do
   316             case "${url}" in
   317                 *)  CT_DoLog DEBUG "Trying \"${url}/${file}${ext}\""
   318                     CT_DoGetFile "${url}/${file}${ext}"
   319                     ;;
   320             esac
   321             [ -f "${file}${ext}" ] && return 0 || true
   322         done
   323     done
   324     CT_Popd
   325 
   326     CT_Abort "Could not download \"${file}\", and not present in \"${CT_LOCAL_TARBALLS_DIR}\""
   327 }
   328 
   329 # Extract a tarball and patch the resulting sources if necessary.
   330 # Some tarballs need to be extracted in specific places. Eg.: glibc addons
   331 # must be extracted in the glibc directory; uCLibc locales must be extracted
   332 # in the extra/locale sub-directory of uClibc.
   333 CT_ExtractAndPatch() {
   334     local file="$1"
   335     local base_file=`echo "${file}" |cut -d - -f 1`
   336     local ver_file=`echo "${file}" |cut -d - -f 2-`
   337     local official_patch_dir
   338     local custom_patch_dir
   339     local libc_addon
   340     local ext=`CT_GetFileExtension "${file}"`
   341     CT_TestAndAbort "\"${file}\" not found in \"${CT_TARBALLS_DIR}\"" -z "${ext}"
   342     local full_file="${CT_TARBALLS_DIR}/${file}${ext}"
   343 
   344     CT_Pushd "${CT_SRC_DIR}"
   345 
   346     # Add-ons need a little love, really.
   347     case "${file}" in
   348         glibc-[a-z]*-*)
   349             CT_TestAndAbort "Trying to extract the C-library addon/locales \"${file}\" when C-library not yet extracted" ! -d "${CT_LIBC_FILE}"
   350             cd "${CT_LIBC_FILE}"
   351             libc_addon=y
   352             [ -f ".${file}.extracted" ] && return 0
   353             touch ".${file}.extracted"
   354             ;;
   355         uClibc-locale-*)
   356             CT_TestAndAbort "Trying to extract the C-library addon/locales \"${file}\" when C-library not yet extracted" ! -d "${CT_LIBC_FILE}"
   357             cd "${CT_LIBC_FILE}/extra/locale"
   358             libc_addon=y
   359             [ -f ".${file}.extracted" ] && return 0
   360             touch ".${file}.extracted"
   361             ;;
   362     esac
   363 
   364     # If the directory exists, then consider extraction and patching done
   365     if [ -d "${file}" ]; then
   366         CT_DoLog DEBUG "Already extracted \"${file}\""
   367         return 0
   368     fi
   369 
   370     CT_DoLog EXTRA "Extracting \"${file}\""
   371     case "${ext}" in
   372         .tar.bz2)     tar xvjf "${full_file}" |CT_DoLog ALL;;
   373         .tar.gz|.tgz) tar xvzf "${full_file}" |CT_DoLog ALL;;
   374         .tar)         tar xvf  "${full_file}" |CT_DoLog ALL;;
   375         *)            CT_Abort "Don't know how to handle \"${file}\": unknown extension" ;;
   376     esac
   377 
   378     # Snapshots might not have the version number in the extracted directory
   379     # name. This is also the case for some (old) packages, such as libfloat.
   380     # Overcome this issue by symlink'ing the directory.
   381     if [ ! -d "${file}" -a "${libc_addon}" != "y" ]; then
   382         case "${ext}" in
   383             .tar.bz2)     base=`tar tjf "${full_file}" |head -n 1 |cut -d / -f 1 || true`;;
   384             .tar.gz|.tgz) base=`tar tzf "${full_file}" |head -n 1 |cut -d / -f 1 || true`;;
   385             .tar)         base=`tar tf  "${full_file}" |head -n 1 |cut -d / -f 1 || true`;;
   386         esac
   387         CT_TestOrAbort "There was a problem when extracting \"${file}\"" -d "${base}" -o "${base}" != "${file}"
   388         ln -s "${base}" "${file}"
   389     fi
   390 
   391     # Kludge: outside this function, we wouldn't know if we had just extracted
   392     # a libc addon, or a plain package. Apply patches now.
   393     CT_DoLog EXTRA "Patching \"${file}\""
   394 
   395     # If libc addon, we're already in the correct place.
   396     [ -z "${libc_addon}" ] && cd "${file}"
   397 
   398     [ "${CUSTOM_PATCH_ONLY}" = "y" ] || official_patch_dir="${CT_TOP_DIR}/patches/${base_file}/${ver_file}"
   399     [ "${CT_CUSTOM_PATCH}" = "y" ] && custom_patch_dir="${CT_CUSTOM_PATCH_DIR}/${base_file}/${ver_file}"
   400     for patch_dir in "${official_patch_dir}" "${custom_patch_dir}"; do
   401         if [ -n "${patch_dir}" -a -d "${patch_dir}" ]; then
   402             for p in "${patch_dir}"/*.patch; do
   403                 if [ -f "${p}" ]; then
   404                     CT_DoLog DEBUG "Applying patch \"${p}\""
   405                     patch -g0 -F1 -p1 -f <"${p}" |CT_DoLog ALL
   406                     CT_TestAndAbort "Failed while applying patch file \"${p}\"" ${PIPESTATUS[0]} -ne 0
   407                 fi
   408             done
   409         fi
   410     done
   411 
   412     CT_Popd
   413 }
   414 
   415 # Compute the target triplet from what is provided by the user
   416 # Usage: CT_DoBuildTargetTriplet
   417 # In fact this function takes the environment variables to build the target
   418 # triplet. It is needed both by the normal build sequence, as well as the
   419 # sample saving sequence.
   420 CT_DoBuildTargetTriplet() {
   421     case "${CT_ARCH_BE},${CT_ARCH_LE}" in
   422         y,) target_endian_eb=eb; target_endian_el=;;
   423         ,y) target_endian_eb=; target_endian_el=el;;
   424     esac
   425     case "${CT_ARCH}" in
   426         arm)  CT_TARGET="${CT_ARCH}${target_endian_eb}";;
   427         mips) CT_TARGET="${CT_ARCH}${target_endian_el}";;
   428         x86*) # Much love for this one :-(
   429               # Ultimately, we should use config.sub to output the correct
   430               # procesor name. Work for later...
   431               arch="${CT_ARCH_ARCH}"
   432               [ -z "${arch}" ] && arch="${CT_ARCH_TUNE}"
   433               case "${CT_ARCH}" in
   434                   x86_64)      CT_TARGET=x86_64;;
   435               	  *)  case "${arch}" in
   436                           "")                                       CT_TARGET=i386;;
   437                           i386|i486|i586|i686)                      CT_TARGET="${arch}";;
   438                           winchip*)                                 CT_TARGET=i486;;
   439                           pentium|pentium-mmx|c3*)                  CT_TARGET=i586;;
   440                           nocona|athlon*64|k8|athlon-fx|opteron)    CT_TARGET=x86_64;;
   441                           pentiumpro|pentium*|athlon*)              CT_TARGET=i686;;
   442                           *)                                        CT_TARGET=i586;;
   443                       esac;;
   444               esac;;
   445     esac
   446     case "${CT_TARGET_VENDOR}" in
   447         "") CT_TARGET="${CT_TARGET}-unknown";;
   448         *)  CT_TARGET="${CT_TARGET}-${CT_TARGET_VENDOR}";;
   449     esac
   450     case "${CT_KERNEL}" in
   451         linux*)  CT_TARGET="${CT_TARGET}-linux";;
   452         cygwin*) CT_TARGET="${CT_TARGET}-cygwin";;
   453     esac
   454     case "${CT_LIBC}" in
   455         glibc)  CT_TARGET="${CT_TARGET}-gnu";;
   456         uClibc) CT_TARGET="${CT_TARGET}-uclibc";;
   457     esac
   458     case "${CT_ARCH_ABI}" in
   459         eabi)   CT_TARGET="${CT_TARGET}eabi";;
   460     esac
   461     CT_TARGET="`${CT_TOP_DIR}/tools/config.sub ${CT_TARGET}`"
   462 }
   463 
   464 # This function does pause the build until the user strikes "Return"
   465 # Usage: CT_DoPause [optional_message]
   466 CT_DoPause() {
   467     local foo
   468     local message="${1:-Pausing for your pleasure}"
   469     CT_DoLog INFO "${message}"
   470     read -p "Press \"Enter\" to continue, or Ctrl-C to stop..." foo >&6
   471     return 0
   472 }
   473 
   474 # This function saves the state of the toolchain to be able to restart
   475 # at any one point
   476 # Usage: CT_DoSaveState <next_step_name>
   477 CT_DoSaveState() {
   478 	[ "${CT_DEBUG_CT_SAVE_STEPS}" = "y" ] || return 0
   479     local state_name="$1"
   480     local state_dir="${CT_STATE_DIR}/${state_name}"
   481 
   482     CT_DoLog DEBUG "Saving state to restart at step \"${state_name}\"..."
   483     rm -rf "${state_dir}"
   484     mkdir -p "${state_dir}"
   485 
   486     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   487         y)  tar_opt=czf; tar_ext=".tar.gz";;
   488         *)  tar_opt=cf;  tar_ext=".tar";;
   489     esac
   490 
   491     CT_DoLog DEBUG "  Saving environment and aliases"
   492     # We must omit shell functions
   493     # 'isgrep' is here because I don't seem to
   494     # be able to remove the functions names.
   495     set |awk '
   496          BEGIN { _p = 1; }
   497          $0~/^[^ ] ()/ { _p = 0; }
   498          _p == 1
   499          $0 == "}" { _p = 1; }
   500          ' |egrep -v '^[^ ]+ \(\)' >"${state_dir}/env.sh"
   501 
   502     CT_DoLog DEBUG "  Saving CT_CC_CORE_PREFIX_DIR=\"${CT_CC_CORE_PREFIX_DIR}\""
   503     CT_Pushd "${CT_CC_CORE_PREFIX_DIR}"
   504     tar ${tar_opt} "${state_dir}/cc_core_prefix_dir${tar_ext}" .
   505     CT_Popd
   506 
   507     CT_DoLog DEBUG "  Saving CT_PREFIX_DIR=\"${CT_PREFIX_DIR}\""
   508     CT_Pushd "${CT_PREFIX_DIR}"
   509     tar ${tar_opt} "${state_dir}/prefix_dir${tar_ext}" .
   510     CT_Popd
   511 
   512     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
   513         CT_DoLog DEBUG "  Saving log file"
   514         exec >/dev/null
   515         case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   516             y)  gzip -3 -c "${CT_LOG_FILE}"  >"${state_dir}/log.gz";;
   517             *)  cat "${CT_LOG_FILE}" >"${state_dir}/log";;
   518         esac
   519         exec >>"${CT_LOG_FILE}"
   520     fi
   521 }
   522 
   523 # This functions restores a previously saved state
   524 # Usage: CT_DoLoadState <state_name>
   525 CT_DoLoadState(){
   526     local state_name="$1"
   527     local state_dir="${CT_STATE_DIR}/${state_name}"
   528 
   529     # We need to do something special with the log file!
   530     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
   531         exec >"${state_dir}/tail.log"
   532     fi
   533     CT_DoLog DEBUG "Restoring state at step \"${state_name}\"..."
   534 
   535     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   536         y)  tar_opt=xzf; tar_ext=".tar.gz";;
   537         *)  tar_opt=cf;  tar_ext=".tar";;
   538     esac
   539 
   540     CT_DoLog DEBUG "  Removing previous build directories"
   541     chmod -R u+rwX "${CT_PREFIX_DIR}" "${CT_CC_CORE_PREFIX_DIR}"
   542     rm -rf         "${CT_PREFIX_DIR}" "${CT_CC_CORE_PREFIX_DIR}"
   543     mkdir -p       "${CT_PREFIX_DIR}" "${CT_CC_CORE_PREFIX_DIR}"
   544 
   545     CT_DoLog DEBUG "  Restoring CT_PREFIX_DIR=\"${CT_PREFIX_DIR}\""
   546     CT_Pushd "${CT_PREFIX_DIR}"
   547     tar ${tar_opt} "${state_dir}/prefix_dir${tar_ext}"
   548     CT_Popd
   549 
   550     CT_DoLog DEBUG "  Restoring CT_CC_CORE_PREFIX_DIR=\"${CT_CC_CORE_PREFIX_DIR}\""
   551     CT_Pushd "${CT_CC_CORE_PREFIX_DIR}"
   552     tar ${tar_opt} "${state_dir}/cc_core_prefix_dir${tar_ext}"
   553     CT_Popd
   554 
   555     # Restore the environment, discarding any error message
   556     # (for example, read-only bash internals)
   557     CT_DoLog DEBUG "  Restoring environment"
   558     . "${state_dir}/env.sh" >/dev/null 2>&1 || true
   559 
   560     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
   561         CT_DoLog DEBUG "  Restoring log file"
   562         exec >/dev/null
   563         case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   564             y)  zcat "${state_dir}/log.gz" >"${CT_LOG_FILE}";;
   565             *)  cat "${state_dir}/log" >"${CT_LOG_FILE}";;
   566         esac
   567         cat "${state_dir}/tail.log" >>"${CT_LOG_FILE}"
   568         exec >>"${CT_LOG_FILE}"
   569         rm -f "${state_dir}/tail.log"
   570     fi
   571 }