scripts/functions
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Wed Aug 10 23:09:06 2011 +0200 (2011-08-10)
changeset 2599 c096c896e745
parent 2595 d6b2354d9d17
child 2605 09b546182d59
permissions -rw-r--r--
scripts/functions: remove messages about failed downloads

When downloading via svn/cvs/... an attempt to retrieve from the
mirror is made. If the mirror does not have the required tarball,
an error message is printed. This is misleading, as the download
may later succeed via svn/cvs/...

Remove the messages about failed downloads altogether.

At the same time, use "if ... then ... fi" instead of "... && ..."

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