scripts/functions
author "Yann E. MORIN" <yann.morin.1998@free.fr>
Thu Dec 27 12:53:32 2012 +0100 (2012-12-27)
changeset 3153 f6740f9e42de
parent 3097 5c67476c7342
child 3169 9d0b37f08a10
permissions -rw-r--r--
scripts/addToolsVersion: handle elf2flt

The one was missing from the list.

It is very improbable that we ever need it, as elf2flt does no release,
and we always get it from CVS head. But for the sake of consistency, we
just add it.

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