scripts/functions
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Mon Mar 29 00:15:32 2010 +0200 (2010-03-29)
changeset 1864 758d5137fe87
parent 1765 93cb44d28002
child 1894 faea3afad5c0
permissions -rw-r--r--
scripts/populate: optimise search loop

Curently, populate will iterate over all ELF (shared objects|executables)
to look for missing NEEDED DSOs, adding to the list at every iterations
of the search loop.

Instead of looking again at previously handled ELF files, recursively
resolve every ELf files.

Also, in case there are a whole lot of files (more than the shell can
accept as arguments list, or creating a command line longer than the
shell can cope with), use a temporary file with the list of files
to search for missing dependencies.
     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 # 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 /.git ''; do
   312         if [ -e "${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     # Do *not* try git trees (ext=/.git), this is handled in a specific
   474     # wrapper, below
   475     for ext in ${first_ext} .tar.bz2 .tar.gz .tgz .tar ''; do
   476         # Try all urls in turn
   477         for url in ${URLS}; do
   478             CT_DoLog DEBUG "Trying '${url}/${file}${ext}'"
   479             CT_DoGetFile "${url}/${file}${ext}"
   480             if [ -f "${file}${ext}" ]; then
   481                 CT_DoLog DEBUG "Got '${file}' from the Internet"
   482                 CT_SaveLocal "${CT_TARBALLS_DIR}/${file}${ext}"
   483                 return 0
   484             fi
   485         done
   486     done
   487     CT_Popd
   488 
   489     CT_Abort "Could not retrieve '${file}'."
   490 }
   491 
   492 # Checkout from CVS, and build the associated tarball
   493 # The tarball will be called ${basename}.tar.bz2
   494 # Prerequisite: either the server does not require password,
   495 # or the user must already be logged in.
   496 # 'tag' is the tag to retrieve. Must be specified, but can be empty.
   497 # If dirname is specified, then module will be renamed to dirname
   498 # prior to building the tarball.
   499 # Usage: CT_GetCVS <basename> <url> <module> <tag> [dirname[=subdir]]
   500 # Note: if '=subdir' is given, then it is used instead of 'module'.
   501 CT_GetCVS() {
   502     local basename="$1"
   503     local uri="$2"
   504     local module="$3"
   505     local tag="${4:+-r ${4}}"
   506     local dirname="$5"
   507     local tmp_dir
   508 
   509     # Does it exist localy?
   510     CT_GetLocal "${basename}" && return 0 || true
   511     # No, it does not...
   512 
   513     CT_DoLog EXTRA "Retrieving '${basename}'"
   514 
   515     CT_MktempDir tmp_dir
   516     CT_Pushd "${tmp_dir}"
   517 
   518     CT_DoExecLog ALL cvs -z 9 -d "${uri}" co -P ${tag} "${module}"
   519     if [ -n "${dirname}" ]; then
   520         case "${dirname}" in
   521             *=*)
   522                 CT_DoExecLog DEBUG mv "${dirname#*=}" "${dirname%%=*}"
   523                 CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${dirname%%=*}"
   524                 ;;
   525             *)
   526                 CT_DoExecLog ALL mv "${module}" "${dirname}"
   527                 CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${dirname:-${module}}"
   528                 ;;
   529         esac
   530     fi
   531     CT_SaveLocal "${CT_TARBALLS_DIR}/${basename}.tar.bz2"
   532 
   533     CT_Popd
   534     CT_DoExecLog ALL rm -rf "${tmp_dir}"
   535 }
   536 
   537 # Check out from SVN, and build the associated tarball
   538 # The tarball will be called ${basename}.tar.bz2
   539 # Prerequisite: either the server does not require password,
   540 # or the user must already be logged in.
   541 # 'rev' is the revision to retrieve
   542 # Usage: CT_GetSVN <basename> <url> [rev]
   543 CT_GetSVN() {
   544     local basename="$1"
   545     local uri="$2"
   546     local rev="$3"
   547 
   548     # Does it exist localy?
   549     CT_GetLocal "${basename}" && return 0 || true
   550     # No, it does not...
   551 
   552     CT_DoLog EXTRA "Retrieving '${basename}'"
   553 
   554     CT_MktempDir tmp_dir
   555     CT_Pushd "${tmp_dir}"
   556 
   557     CT_DoExecLog ALL svn export ${rev:+-r ${rev}} "${uri}" "${basename}"
   558     CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${basename}"
   559     CT_SaveLocal "${CT_TARBALLS_DIR}/${basename}.tar.bz2"
   560 
   561     CT_Popd
   562     CT_DoExecLog ALL rm -rf "${tmp_dir}"
   563 }
   564 
   565 # Clone a git tree
   566 # Tries the given URLs in turn until one can get cloned. No tarball will be created.
   567 # Prerequisites: either the server does not require password,
   568 # or the user has already taken any action to authenticate to the server.
   569 # The cloned tree will *not* be stored in the local tarballs dir!
   570 # Usage: CT_GetGit <basename> <url [url ...]>
   571 CT_GetGit() {
   572     local basename="$1"; shift
   573     local url
   574     local cloned=0
   575 
   576     # Do we have it in our tarballs dir?
   577     if [ -d "${CT_TARBALLS_DIR}/${basename}/.git" ]; then
   578         CT_DoLog EXTRA "Updating git tree '${basename}'"
   579         CT_Pushd "${CT_TARBALLS_DIR}/${basename}"
   580         CT_DoExecLog ALL git pull
   581         CT_Popd
   582     else
   583         CT_DoLog EXTRA "Retrieving git tree '${basename}'"
   584         for url in "${@}"; do
   585             CT_DoLog ALL "Trying to clone from '${url}'"
   586             CT_DoForceRmdir "${CT_TARBALLS_DIR}/${basename}"
   587             if git clone "${url}" "${CT_TARBALLS_DIR}/${basename}" 2>&1 |CT_DoLog ALL; then
   588                 cloned=1
   589                 break
   590             fi
   591         done
   592         CT_TestOrAbort "Could not clone '${basename}'" ${cloned} -ne 0
   593     fi
   594 }
   595 
   596 # Extract a tarball
   597 # Some tarballs need to be extracted in specific places. Eg.: glibc addons
   598 # must be extracted in the glibc directory; uCLibc locales must be extracted
   599 # in the extra/locale sub-directory of uClibc. This is taken into account
   600 # by the caller, that did a 'cd' into the correct path before calling us
   601 # and sets nochdir to 'nochdir'.
   602 # Note also that this function handles the git trees!
   603 # Usage: CT_Extract <basename> [nochdir] [options]
   604 # where 'options' are dependent on the source (eg. git branch/tag...)
   605 CT_Extract() {
   606     local nochdir="$1"
   607     local basename
   608     local ext
   609 
   610     if [ "${nochdir}" = "nochdir" ]; then
   611         shift
   612         nochdir="$(pwd)"
   613     else
   614         nochdir="${CT_SRC_DIR}"
   615     fi
   616 
   617     basename="$1"
   618     shift
   619 
   620     if ! ext="$(CT_GetFileExtension "${basename}")"; then
   621       CT_Abort "'${basename}' not found in '${CT_TARBALLS_DIR}'"
   622     fi
   623     local full_file="${CT_TARBALLS_DIR}/${basename}${ext}"
   624 
   625     # Check if already extracted
   626     if [ -e "${CT_SRC_DIR}/.${basename}.extracted" ]; then
   627         CT_DoLog DEBUG "Already extracted '${basename}'"
   628         return 0
   629     fi
   630 
   631     # Check if previously partially extracted
   632     if [ -e "${CT_SRC_DIR}/.${basename}.extracting" ]; then
   633         CT_DoLog ERROR "The '${basename}' sources were partially extracted."
   634         CT_DoLog ERROR "Please remove first:"
   635         CT_DoLog ERROR " - the source dir for '${basename}', in '${CT_SRC_DIR}'"
   636         CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${basename}.extracting'"
   637         CT_Abort "I'll stop now to avoid any carnage..."
   638     fi
   639     CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${basename}.extracting"
   640 
   641     CT_Pushd "${nochdir}"
   642 
   643     CT_DoLog EXTRA "Extracting '${basename}'"
   644     case "${ext}" in
   645         .tar.bz2)     CT_DoExecLog ALL tar xvjf "${full_file}";;
   646         .tar.gz|.tgz) CT_DoExecLog ALL tar xvzf "${full_file}";;
   647         .tar)         CT_DoExecLog ALL tar xvf  "${full_file}";;
   648         /.git)        CT_ExtractGit "${basename}" "${@}";;
   649         *)            CT_Abort "Don't know how to handle '${basename}${ext}': unknown extension";;
   650     esac
   651 
   652     # Some tarballs have read-only files... :-(
   653     # Because of nochdir, we don't know where we are, so chmod all
   654     # the src tree
   655     CT_DoExecLog DEBUG chmod -R u+w "${CT_SRC_DIR}"
   656 
   657     # Don't mark as being extracted for git
   658     case "${ext}" in
   659         /.git)  ;;
   660         *)      CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${basename}.extracted";;
   661     esac
   662     CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/.${basename}.extracting"
   663 
   664     CT_Popd
   665 }
   666 
   667 # Create a working git clone
   668 # Usage: CT_ExtractGit <basename> [ref]
   669 # where 'ref' is the reference to use:
   670 #   the full name of a branch, like "remotes/origin/branch_name"
   671 #   a date as understandable by git, like "YYYY-MM-DD[ hh[:mm[:ss]]]"
   672 #   a tag name
   673 CT_ExtractGit() {
   674     local basename="${1}"
   675     local ref="${2}"
   676     local clone_dir
   677     local ref_type
   678 
   679     # pushd now to be able to get git revlist in case ref is a date
   680     clone_dir="${CT_TARBALLS_DIR}/${basename}"
   681     CT_Pushd "${clone_dir}"
   682 
   683     # What kind of reference is ${ref} ?
   684     if [ -z "${ref}" ]; then
   685         # Don't update the clone, keep as-is
   686         ref_type=none
   687     elif git tag |grep -E "^${ref}$" >/dev/null 2>&1; then
   688         ref_type=tag
   689     elif git branch -a --no-color |grep -E "^. ${ref}$" >/dev/null 2>&1; then
   690         ref_type=branch
   691     elif date -d "${ref}" >/dev/null 2>&1; then
   692         ref_type=date
   693         ref=$(git rev-list -n1 --before="${ref}")
   694     else
   695         CT_Abort "Reference '${ref}' is an incorrect git reference: neither tag, branch nor date"
   696     fi
   697 
   698     CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/${basename}"
   699     CT_DoExecLog ALL ln -sf "${clone_dir}" "${CT_SRC_DIR}/${basename}"
   700 
   701     case "${ref_type}" in
   702         none)   ;;
   703         *)      CT_DoExecLog ALL git checkout "${ref}";;
   704     esac
   705 
   706     CT_Popd
   707 }
   708 
   709 # Patches the specified component
   710 # See CT_Extract, above, for explanations on 'nochdir'
   711 # Usage: CT_Patch [nochdir] <basename>
   712 CT_Patch() {
   713     local nochdir="$1"
   714     local basename
   715     local base_file
   716     local ver_file
   717     local d
   718     local -a patch_dirs
   719     local bundled_patch_dir
   720     local local_patch_dir
   721 
   722     if [ "${nochdir}" = "nochdir" ]; then
   723         shift
   724         nochdir="$(pwd)"
   725     else
   726         nochdir="${CT_SRC_DIR}/${1}"
   727     fi
   728 
   729     basename="$1"
   730     base_file="${basename%%-*}"
   731     ver_file="${basename#*-}"
   732 
   733     # Check if already patched
   734     if [ -e "${CT_SRC_DIR}/.${basename}.patched" ]; then
   735         CT_DoLog DEBUG "Already patched '${basename}'"
   736         return 0
   737     fi
   738 
   739     # Check if already partially patched
   740     if [ -e "${CT_SRC_DIR}/.${basename}.patching" ]; then
   741         CT_DoLog ERROR "The '${basename}' sources were partially patched."
   742         CT_DoLog ERROR "Please remove first:"
   743         CT_DoLog ERROR " - the source dir for '${basename}', in '${CT_SRC_DIR}'"
   744         CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${basename}.extracted'"
   745         CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${basename}.patching'"
   746         CT_Abort "I'll stop now to avoid any carnage..."
   747     fi
   748     touch "${CT_SRC_DIR}/.${basename}.patching"
   749 
   750     CT_Pushd "${nochdir}"
   751 
   752     CT_DoLog EXTRA "Patching '${basename}'"
   753 
   754     bundled_patch_dir="${CT_LIB_DIR}/patches/${base_file}/${ver_file}"
   755     local_patch_dir="${CT_LOCAL_PATCH_DIR}/${base_file}/${ver_file}"
   756 
   757     case "${CT_PATCH_ORDER}" in
   758         bundled)        patch_dirs=("${bundled_patch_dir}");;
   759         local)          patch_dirs=("${local_patch_dir}");;
   760         bundled,local)  patch_dirs=("${bundled_patch_dir}" "${local_patch_dir}");;
   761         local,bundled)  patch_dirs=("${local_patch_dir}" "${bundled_patch_dir}");;
   762         none)           patch_dirs=;;
   763     esac
   764 
   765     for d in "${patch_dirs[@]}"; do
   766         CT_DoLog DEBUG "Looking for patches in '${d}'..."
   767         if [ -n "${d}" -a -d "${d}" ]; then
   768             for p in "${d}"/*.patch; do
   769                 if [ -f "${p}" ]; then
   770                     CT_DoLog DEBUG "Applying patch '${p}'"
   771                     CT_DoExecLog ALL patch --no-backup-if-mismatch -g0 -F1 -p1 -f <"${p}"
   772                 fi
   773             done
   774             if [ "${CT_PATCH_SINGLE}" = "y" ]; then
   775                 break
   776             fi
   777         fi
   778     done
   779 
   780     if [ "${CT_OVERIDE_CONFIG_GUESS_SUB}" = "y" ]; then
   781         CT_DoLog ALL "Overiding config.guess and config.sub"
   782         for cfg in config_guess config_sub; do
   783             eval ${cfg}="${CT_LIB_DIR}/scripts/${cfg/_/.}"
   784             [ -e "${CT_TOP_DIR}/scripts/${cfg/_/.}" ] && eval ${cfg}="${CT_TOP_DIR}/scripts/${cfg/_/.}"
   785             # Can't use CT_DoExecLog because of the '{} \;' to be passed un-mangled to find
   786             find . -type f -name "${cfg/_/.}" -exec cp -v "${!cfg}" {} \; |CT_DoLog ALL
   787         done
   788     fi
   789 
   790     CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${basename}.patched"
   791     CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/.${basename}.patching"
   792 
   793     CT_Popd
   794 }
   795 
   796 # Two wrappers to call config.(guess|sub) either from CT_TOP_DIR or CT_LIB_DIR.
   797 # Those from CT_TOP_DIR, if they exist, will be be more recent than those from CT_LIB_DIR.
   798 CT_DoConfigGuess() {
   799     if [ -x "${CT_TOP_DIR}/scripts/config.guess" ]; then
   800         "${CT_TOP_DIR}/scripts/config.guess"
   801     else
   802         "${CT_LIB_DIR}/scripts/config.guess"
   803     fi
   804 }
   805 
   806 CT_DoConfigSub() {
   807     if [ -x "${CT_TOP_DIR}/scripts/config.sub" ]; then
   808         "${CT_TOP_DIR}/scripts/config.sub" "$@"
   809     else
   810         "${CT_LIB_DIR}/scripts/config.sub" "$@"
   811     fi
   812 }
   813 
   814 # Compute the target tuple from what is provided by the user
   815 # Usage: CT_DoBuildTargetTuple
   816 # In fact this function takes the environment variables to build the target
   817 # tuple. It is needed both by the normal build sequence, as well as the
   818 # sample saving sequence.
   819 CT_DoBuildTargetTuple() {
   820     # Set the endianness suffix, and the default endianness gcc option
   821     case "${CT_ARCH_BE},${CT_ARCH_LE}" in
   822         y,) target_endian_eb=eb
   823             target_endian_el=
   824             CT_ARCH_ENDIAN_CFLAG="-mbig-endian"
   825             CT_ARCH_ENDIAN_LDFLAG="-EB"
   826             ;;
   827         ,y) target_endian_eb=
   828             target_endian_el=el
   829             CT_ARCH_ENDIAN_CFLAG="-mlittle-endian"
   830             CT_ARCH_ENDIAN_LDFLAG="-EL"
   831             ;;
   832     esac
   833 
   834     # Build the default architecture tuple part
   835     CT_TARGET_ARCH="${CT_ARCH}"
   836 
   837     # Set defaults for the system part of the tuple. Can be overriden
   838     # by architecture-specific values.
   839     case "${CT_LIBC}" in
   840         *glibc) CT_TARGET_SYS=gnu;;
   841         uClibc) CT_TARGET_SYS=uclibc;;
   842         *)      CT_TARGET_SYS=elf;;
   843     esac
   844 
   845     # Set the default values for ARCH, ABI, CPU, TUNE, FPU and FLOAT
   846     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
   847     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
   848     [ "${CT_ARCH_ARCH}"     ] && { CT_ARCH_ARCH_CFLAG="-march=${CT_ARCH_ARCH}";  CT_ARCH_WITH_ARCH="--with-arch=${CT_ARCH_ARCH}"; }
   849     [ "${CT_ARCH_ABI}"      ] && { CT_ARCH_ABI_CFLAG="-mabi=${CT_ARCH_ABI}";     CT_ARCH_WITH_ABI="--with-abi=${CT_ARCH_ABI}";    }
   850     [ "${CT_ARCH_CPU}"      ] && { CT_ARCH_CPU_CFLAG="-mcpu=${CT_ARCH_CPU}";     CT_ARCH_WITH_CPU="--with-cpu=${CT_ARCH_CPU}";    }
   851     [ "${CT_ARCH_TUNE}"     ] && { CT_ARCH_TUNE_CFLAG="-mtune=${CT_ARCH_TUNE}";  CT_ARCH_WITH_TUNE="--with-tune=${CT_ARCH_TUNE}"; }
   852     [ "${CT_ARCH_FPU}"      ] && { CT_ARCH_FPU_CFLAG="-mfpu=${CT_ARCH_FPU}";     CT_ARCH_WITH_FPU="--with-fpu=${CT_ARCH_FPU}";    }
   853     [ "${CT_ARCH_FLOAT_SW}" ] && { CT_ARCH_FLOAT_CFLAG="-msoft-float";           CT_ARCH_WITH_FLOAT="--with-float=soft";          }
   854 
   855     # Build the default kernel tuple part
   856     CT_TARGET_KERNEL="${CT_KERNEL}"
   857 
   858     # Overide the default values with the components specific settings
   859     CT_DoArchTupleValues
   860     CT_DoKernelTupleValues
   861 
   862     # Finish the target tuple construction
   863     CT_TARGET="${CT_TARGET_ARCH}-"
   864     CT_TARGET="${CT_TARGET}${CT_TARGET_VENDOR:+${CT_TARGET_VENDOR}-}"
   865     CT_TARGET="${CT_TARGET}${CT_TARGET_KERNEL:+${CT_TARGET_KERNEL}-}"
   866     CT_TARGET="${CT_TARGET}${CT_TARGET_SYS}"
   867 
   868     # Sanity checks
   869     __sed_alias=""
   870     if [ -n "${CT_TARGET_ALIAS_SED_EXPR}" ]; then
   871         __sed_alias=$(echo "${CT_TARGET}" |sed -r -e "${CT_TARGET_ALIAS_SED_EXPR}")
   872     fi
   873     case ":${CT_TARGET_VENDOR}:${CT_TARGET_ALIAS}:${__sed_alias}:" in
   874       :*" "*:*:*:) CT_Abort "Don't use spaces in the vendor string, it breaks things.";;
   875       :*"-"*:*:*:) CT_Abort "Don't use dashes in the vendor string, it breaks things.";;
   876       :*:*" "*:*:) CT_Abort "Don't use spaces in the target alias, it breaks things.";;
   877       :*:*:*" "*:) CT_Abort "Don't use spaces in the target sed transform, it breaks things.";;
   878     esac
   879 
   880     # Canonicalise it
   881     CT_TARGET=$(CT_DoConfigSub "${CT_TARGET}")
   882     # Prepare the target CFLAGS
   883     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ENDIAN_CFLAG}"
   884     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ARCH_CFLAG}"
   885     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ABI_CFLAG}"
   886     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_CPU_CFLAG}"
   887     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_TUNE_CFLAG}"
   888     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FPU_CFLAG}"
   889     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FLOAT_CFLAG}"
   890 
   891     # Now on for the target LDFLAGS
   892     CT_ARCH_TARGET_LDFLAGS="${CT_ARCH_TARGET_LDFLAGS} ${CT_ARCH_ENDIAN_LDFLAG}"
   893 }
   894 
   895 # This function does pause the build until the user strikes "Return"
   896 # Usage: CT_DoPause [optional_message]
   897 CT_DoPause() {
   898     local foo
   899     local message="${1:-Pausing for your pleasure}"
   900     CT_DoLog INFO "${message}"
   901     read -p "Press 'Enter' to continue, or Ctrl-C to stop..." foo >&6
   902     return 0
   903 }
   904 
   905 # This function saves the state of the toolchain to be able to restart
   906 # at any one point
   907 # Usage: CT_DoSaveState <next_step_name>
   908 CT_DoSaveState() {
   909 	[ "${CT_DEBUG_CT_SAVE_STEPS}" = "y" ] || return 0
   910     local state_name="$1"
   911     local state_dir="${CT_STATE_DIR}/${state_name}"
   912 
   913     # Log this to the log level required by the user
   914     CT_DoLog ${CT_LOG_LEVEL_MAX} "Saving state to restart at step '${state_name}'..."
   915 
   916     rm -rf "${state_dir}"
   917     mkdir -p "${state_dir}"
   918 
   919     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   920         y)  tar_opt=z; tar_ext=.gz;;
   921         *)  tar_opt=;  tar_ext=;;
   922     esac
   923 
   924     CT_DoLog DEBUG "  Saving environment and aliases"
   925     # We must omit shell functions, and some specific bash variables
   926     # that break when restoring the environment, later. We could do
   927     # all the processing in the awk script, but a sed is easier...
   928     set |awk '
   929               BEGIN { _p = 1; }
   930               $0~/^[^ ]+ \(\)/ { _p = 0; }
   931               _p == 1
   932               $0 == "}" { _p = 1; }
   933               ' |sed -r -e '/^BASH_(ARGC|ARGV|LINENO|SOURCE|VERSINFO)=/d;
   934                            /^(UID|EUID)=/d;
   935                            /^(FUNCNAME|GROUPS|PPID|SHELLOPTS)=/d;' >"${state_dir}/env.sh"
   936 
   937     CT_DoLog DEBUG "  Saving CT_CONFIG_DIR='${CT_CONFIG_DIR}'"
   938     CT_Pushd "${CT_CONFIG_DIR}"
   939     CT_DoExecLog DEBUG tar cv${tar_opt}f "${state_dir}/config_dir.tar${tar_ext}" .
   940     CT_Popd
   941 
   942     CT_DoLog DEBUG "  Saving CT_CC_CORE_STATIC_PREFIX_DIR='${CT_CC_CORE_STATIC_PREFIX_DIR}'"
   943     CT_Pushd "${CT_CC_CORE_STATIC_PREFIX_DIR}"
   944     CT_DoExecLog DEBUG tar cv${tar_opt}f "${state_dir}/cc_core_static_prefix_dir.tar${tar_ext}" .
   945     CT_Popd
   946 
   947     CT_DoLog DEBUG "  Saving CT_CC_CORE_SHARED_PREFIX_DIR='${CT_CC_CORE_SHARED_PREFIX_DIR}'"
   948     CT_Pushd "${CT_CC_CORE_SHARED_PREFIX_DIR}"
   949     CT_DoExecLog DEBUG tar cv${tar_opt}f "${state_dir}/cc_core_shared_prefix_dir.tar${tar_ext}" .
   950     CT_Popd
   951 
   952     CT_DoLog DEBUG "  Saving CT_PREFIX_DIR='${CT_PREFIX_DIR}'"
   953     CT_Pushd "${CT_PREFIX_DIR}"
   954     CT_DoExecLog DEBUG tar cv${tar_opt}f "${state_dir}/prefix_dir.tar${tar_ext}" --exclude '*.log' .
   955     CT_Popd
   956 
   957     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
   958         CT_DoLog DEBUG "  Saving log file"
   959         exec >/dev/null
   960         case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   961             y)  gzip -3 -c "${CT_LOG_FILE}"  >"${state_dir}/log.gz";;
   962             *)  cat "${CT_LOG_FILE}" >"${state_dir}/log";;
   963         esac
   964         exec >>"${CT_LOG_FILE}"
   965     fi
   966 }
   967 
   968 # This function restores a previously saved state
   969 # Usage: CT_DoLoadState <state_name>
   970 CT_DoLoadState(){
   971     local state_name="$1"
   972     local state_dir="${CT_STATE_DIR}/${state_name}"
   973     local old_RESTART="${CT_RESTART}"
   974     local old_STOP="${CT_STOP}"
   975 
   976     CT_TestOrAbort "The previous build did not reach the point where it could be restarted at '${CT_RESTART}'" -d "${state_dir}"
   977 
   978     # We need to do something special with the log file!
   979     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
   980         exec >"${state_dir}/tail.log"
   981     fi
   982 
   983     # Log this to the log level required by the user
   984     CT_DoLog ${CT_LOG_LEVEL_MAX} "Restoring state at step '${state_name}', as requested."
   985 
   986     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   987         y)  tar_opt=z; tar_ext=.gz;;
   988         *)  tar_opt=;  tar_ext=;;
   989     esac
   990 
   991     CT_DoLog DEBUG "  Removing previous build directories"
   992     CT_DoForceRmdir             "${CT_PREFIX_DIR}" "${CT_CC_CORE_SHARED_PREFIX_DIR}" "${CT_CC_CORE_STATIC_PREFIX_DIR}" "${CT_CONFIG_DIR}"
   993     CT_DoExecLog DEBUG mkdir -p "${CT_PREFIX_DIR}" "${CT_CC_CORE_SHARED_PREFIX_DIR}" "${CT_CC_CORE_STATIC_PREFIX_DIR}" "${CT_CONFIG_DIR}"
   994 
   995     CT_DoLog DEBUG "  Restoring CT_PREFIX_DIR='${CT_PREFIX_DIR}'"
   996     CT_Pushd "${CT_PREFIX_DIR}"
   997     CT_DoExecLog DEBUG tar xv${tar_opt}f "${state_dir}/prefix_dir.tar${tar_ext}"
   998     CT_Popd
   999 
  1000     CT_DoLog DEBUG "  Restoring CT_CC_CORE_SHARED_PREFIX_DIR='${CT_CC_CORE_SHARED_PREFIX_DIR}'"
  1001     CT_Pushd "${CT_CC_CORE_SHARED_PREFIX_DIR}"
  1002     CT_DoExecLog DEBUG tar xv${tar_opt}f "${state_dir}/cc_core_shared_prefix_dir.tar${tar_ext}"
  1003     CT_Popd
  1004 
  1005     CT_DoLog DEBUG "  Restoring CT_CC_CORE_STATIC_PREFIX_DIR='${CT_CC_CORE_STATIC_PREFIX_DIR}'"
  1006     CT_Pushd "${CT_CC_CORE_STATIC_PREFIX_DIR}"
  1007     CT_DoExecLog DEBUG tar xv${tar_opt}f "${state_dir}/cc_core_static_prefix_dir.tar${tar_ext}"
  1008     CT_Popd
  1009 
  1010     CT_DoLog DEBUG "  Restoring CT_CONFIG_DIR='${CT_CONFIG_DIR}'"
  1011     CT_Pushd "${CT_CONFIG_DIR}"
  1012     CT_DoExecLog DEBUG tar xv${tar_opt}f "${state_dir}/config_dir.tar${tar_ext}"
  1013     CT_Popd
  1014 
  1015     # Restore the environment, discarding any error message
  1016     # (for example, read-only bash internals)
  1017     CT_DoLog DEBUG "  Restoring environment"
  1018     . "${state_dir}/env.sh" >/dev/null 2>&1 || true
  1019 
  1020     # Restore the new RESTART and STOP steps
  1021     CT_RESTART="${old_RESTART}"
  1022     CT_STOP="${old_STOP}"
  1023     unset old_stop old_restart
  1024 
  1025     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
  1026         CT_DoLog DEBUG "  Restoring log file"
  1027         exec >/dev/null
  1028         case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
  1029             y)  zcat "${state_dir}/log.gz" >"${CT_LOG_FILE}";;
  1030             *)  cat "${state_dir}/log" >"${CT_LOG_FILE}";;
  1031         esac
  1032         cat "${state_dir}/tail.log" >>"${CT_LOG_FILE}"
  1033         exec >>"${CT_LOG_FILE}"
  1034         rm -f "${state_dir}/tail.log"
  1035     fi
  1036 }