scripts/functions
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Sun Jan 17 23:06:02 2010 +0100 (2010-01-17)
changeset 1740 c57458bb354d
parent 1718 fa028a13929e
child 1741 2d94844c1a63
permissions -rw-r--r--
configure: do not require hg when configuring in an hg clone

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