scripts/functions
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Wed May 23 21:08:24 2007 +0000 (2007-05-23)
changeset 128 44e133d0cddb
parent 127 2ba6a8d5d4be
child 135 b2695c2f1919
permissions -rw-r--r--
In CT_Abort(), don't send output to stderr. Let the log facility handle where things should go.
     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"
   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               arch="${CT_ARCH_ARCH}"
   430               [ -z "${arch}" ] && arch="${CT_ARCH_TUNE}"
   431               case "${CT_ARCH}" in
   432                   x86_64)  CT_TARGET=x86_64;;
   433               	  *)  case "${arch}" in
   434                           "")                                       CT_TARGET=i386;;
   435                           i386|i486|i586|i686)                      CT_TARGET="${arch}";;
   436                           winchip*)                                 CT_TARGET=i486;;
   437                           pentium|pentium-mmx|c3*)                  CT_TARGET=i586;;
   438                           nocona|athlon*64|k8|athlon-fx|opteron)    CT_TARGET=x86_64;;
   439                           pentiumpro|pentium*|athlon*)              CT_TARGET=i686;;
   440                           *)                                        CT_TARGET=i586;;
   441                       esac;;
   442               esac;;
   443     esac
   444     case "${CT_TARGET_VENDOR}" in
   445         "") CT_TARGET="${CT_TARGET}-unknown";;
   446         *)  CT_TARGET="${CT_TARGET}-${CT_TARGET_VENDOR}";;
   447     esac
   448     case "${CT_KERNEL}" in
   449         linux*)  CT_TARGET="${CT_TARGET}-linux";;
   450     esac
   451     case "${CT_LIBC}" in
   452         glibc)  CT_TARGET="${CT_TARGET}-gnu";;
   453         uClibc) CT_TARGET="${CT_TARGET}-uclibc";;
   454     esac
   455     CT_TARGET="`${CT_TOP_DIR}/tools/config.sub ${CT_TARGET}`"
   456 }
   457 
   458 # This function does pause the build until the user strikes "Return"
   459 # Usage: CT_DoPause [optional_message]
   460 CT_DoPause() {
   461     local foo
   462     local message="${1:-Pausing for your pleasure}"
   463     CT_DoLog INFO "${message}"
   464     read -p "Press \"Enter\" to continue, or Ctrl-C to stop..." foo >&6
   465     return 0
   466 }
   467 
   468 # This function saves the state of the toolchain to be able to restart
   469 # at any one point
   470 # Usage: CT_DoSaveState <next_step_name>
   471 CT_DoSaveState() {
   472 	[ "${CT_DEBUG_CT_SAVE_STEPS}" = "y" ] || return 0
   473     local state_name="$1"
   474     local state_dir="${CT_STATE_DIR}/${state_name}"
   475 
   476     CT_DoLog DEBUG "Saving state to restart at step \"${state_name}\"..."
   477     rm -rf "${state_dir}"
   478     mkdir -p "${state_dir}"
   479 
   480     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   481         y)  tar_opt=czf; tar_ext=".tar.gz";;
   482         *)  tar_opt=cf;  tar_ext=".tar";;
   483     esac
   484 
   485     CT_DoLog DEBUG "  Saving environment and aliases"
   486     # We must omit shell functions
   487     # 'isgrep' is here because I don't seem to
   488     # be able to remove the functions names.
   489     set |awk '
   490          BEGIN { _p = 1; }
   491          $0~/^[^ ] ()/ { _p = 0; }
   492          _p == 1
   493          $0 == "}" { _p = 1; }
   494          ' |egrep -v '^[^ ]+ \(\)' >"${state_dir}/env.sh"
   495 
   496     CT_DoLog DEBUG "  Saving CT_CC_CORE_PREFIX_DIR=\"${CT_CC_CORE_PREFIX_DIR}\""
   497     CT_Pushd "${CT_CC_CORE_PREFIX_DIR}"
   498     tar ${tar_opt} "${state_dir}/cc_core_prefix_dir${tar_ext}" .
   499     CT_Popd
   500 
   501     CT_DoLog DEBUG "  Saving CT_PREFIX_DIR=\"${CT_PREFIX_DIR}\""
   502     CT_Pushd "${CT_PREFIX_DIR}"
   503     tar ${tar_opt} "${state_dir}/prefix_dir${tar_ext}" .
   504     CT_Popd
   505 
   506     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
   507         CT_DoLog DEBUG "  Saving log file"
   508         exec >/dev/null
   509         case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   510             y)  gzip -3 -c "${CT_LOG_FILE}"  >"${state_dir}/log.gz";;
   511             *)  cat "${CT_LOG_FILE}" >"${state_dir}/log";;
   512         esac
   513         exec >>"${CT_LOG_FILE}"
   514     fi
   515 }
   516 
   517 # This functions restores a previously saved state
   518 # Usage: CT_DoLoadState <state_name>
   519 CT_DoLoadState(){
   520     local state_name="$1"
   521     local state_dir="${CT_STATE_DIR}/${state_name}"
   522 
   523     # We need to do something special with the log file!
   524     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
   525         exec >"${state_dir}/tail.log"
   526     fi
   527     CT_DoLog DEBUG "Restoring state at step \"${state_name}\"..."
   528 
   529     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   530         y)  tar_opt=xzf; tar_ext=".tar.gz";;
   531         *)  tar_opt=cf;  tar_ext=".tar";;
   532     esac
   533 
   534     CT_DoLog DEBUG "  Removing previous build directories"
   535     chmod -R u+rwX "${CT_PREFIX_DIR}" "${CT_CC_CORE_PREFIX_DIR}"
   536     rm -rf         "${CT_PREFIX_DIR}" "${CT_CC_CORE_PREFIX_DIR}"
   537     mkdir -p       "${CT_PREFIX_DIR}" "${CT_CC_CORE_PREFIX_DIR}"
   538 
   539     CT_DoLog DEBUG "  Restoring CT_PREFIX_DIR=\"${CT_PREFIX_DIR}\""
   540     CT_Pushd "${CT_PREFIX_DIR}"
   541     tar ${tar_opt} "${state_dir}/prefix_dir${tar_ext}"
   542     CT_Popd
   543 
   544     CT_DoLog DEBUG "  Restoring CT_CC_CORE_PREFIX_DIR=\"${CT_CC_CORE_PREFIX_DIR}\""
   545     CT_Pushd "${CT_CC_CORE_PREFIX_DIR}"
   546     tar ${tar_opt} "${state_dir}/cc_core_prefix_dir${tar_ext}"
   547     CT_Popd
   548 
   549     # Restore the environment, discarding any error message
   550     # (for example, read-only bash internals)
   551     CT_DoLog DEBUG "  Restoring environment"
   552     . "${state_dir}/env.sh" >/dev/null 2>&1 || true
   553 
   554     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
   555         CT_DoLog DEBUG "  Restoring log file"
   556         exec >/dev/null
   557         case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   558             y)  zcat "${state_dir}/log.gz" >"${CT_LOG_FILE}";;
   559             *)  cat "${state_dir}/log" >"${CT_LOG_FILE}";;
   560         esac
   561         cat "${state_dir}/tail.log" >>"${CT_LOG_FILE}"
   562         exec >>"${CT_LOG_FILE}"
   563         rm -f "${state_dir}/tail.log"
   564     fi
   565 }