scripts/functions
author "Yann E. MORIN" <yann.morin.1998@free.fr>
Sun May 11 23:43:52 2014 +0200 (2014-05-11)
changeset 3320 78af1c99bc6d
parent 3314 f3f518cafd8a
child 3321 8a753e6c5621
permissions -rw-r--r--
scripts/functions: add target_endian_le and target_endian_be

We currently define target_endian_el and target_endian_eb to be the
tuple extension depending on endianness, defined to be respectively
'el' or 'eb' according to the endianness.

Some architecture do not use 'el' or 'eb', but use 'le' or 'be'.

Provide that as well, as two new variables: target_endian_le and
target_endian_be.

Signed-off-by: "Yann E. MORIN" <yann.morin.1998@free.fr>
Cc: Cody P Schafer <dev@codyps.com>
     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     CT_DoLog DEBUG "Entering '$1'"
   418     pushd "$1" >/dev/null 2>&1
   419 }
   420 CT_Popd() {
   421     popd >/dev/null 2>&1
   422 }
   423 
   424 # Create a dir and cd or pushd into it
   425 # Usage: CT_mkdir_cd <dir/to/create>
   426 #        CT_mkdir_pushd <dir/to/create>
   427 CT_mkdir_cd() {
   428     local dir="${1}"
   429 
   430     mkdir -p "${dir}"
   431     cd "${dir}"
   432 }
   433 CT_mkdir_pushd() {
   434     local dir="${1}"
   435 
   436     mkdir -p "${dir}"
   437     CT_Pushd "${dir}"
   438 }
   439 
   440 # Creates a temporary directory
   441 # $1: variable to assign to
   442 # Usage: CT_MktempDir foo
   443 CT_MktempDir() {
   444     # Some mktemp do not allow more than 6 Xs
   445     eval "$1"=$(mktemp -q -d "${CT_BUILD_DIR}/tmp.XXXXXX")
   446     CT_TestOrAbort "Could not make temporary directory" -n "${!1}" -a -d "${!1}"
   447     CT_DoLog DEBUG "Made temporary directory '${!1}'"
   448     return 0
   449 }
   450 
   451 # Removes one or more directories, even if it is read-only, or its parent is
   452 # Usage: CT_DoForceRmdir dir [...]
   453 CT_DoForceRmdir() {
   454     local dir
   455     local mode
   456     for dir in "${@}"; do
   457         [ -d "${dir}" ] || continue
   458         case "$CT_SYS_OS" in
   459             Linux|CYGWIN*)
   460                 mode="$(stat -c '%a' "$(dirname "${dir}")")"
   461                 ;;
   462             Darwin|*BSD)
   463                 mode="$(stat -f '%Lp' "$(dirname "${dir}")")"
   464                 ;;
   465             *)
   466                 CT_Abort "Unhandled host OS $CT_SYS_OS"
   467                 ;;
   468         esac
   469         CT_DoExecLog ALL chmod u+w "$(dirname "${dir}")"
   470         CT_DoExecLog ALL chmod -R u+w "${dir}"
   471         CT_DoExecLog ALL rm -rf "${dir}"
   472         CT_DoExecLog ALL chmod ${mode} "$(dirname "${dir}")"
   473     done
   474 }
   475 
   476 # Echoes the specified string on stdout until the pipe breaks.
   477 # Doesn't fail
   478 # $1: string to echo
   479 # Usage: CT_DoYes "" |make oldconfig
   480 CT_DoYes() {
   481     yes "$1" || true
   482 }
   483 
   484 # Add the specified directory to LD_LIBRARY_PATH, and export it
   485 # If the specified patch is already present, just export
   486 # $1: path to add
   487 # $2: add as 'first' or 'last' path, 'first' is assumed if $2 is empty
   488 # Usage CT_SetLibPath /some/where/lib [first|last]
   489 CT_SetLibPath() {
   490     local path="$1"
   491     local pos="$2"
   492 
   493     case ":${LD_LIBRARY_PATH}:" in
   494         *:"${path}":*)  ;;
   495         *)  case "${pos}" in
   496                 last)
   497                     CT_DoLog DEBUG "Adding '${path}' at end of LD_LIBRARY_PATH"
   498                     LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}${path}"
   499                     ;;
   500                 first|"")
   501                     CT_DoLog DEBUG "Adding '${path}' at start of LD_LIBRARY_PATH"
   502                     LD_LIBRARY_PATH="${path}${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
   503                     ;;
   504                 *)
   505                     CT_Abort "Incorrect position '${pos}' to add '${path}' to LD_LIBRARY_PATH"
   506                     ;;
   507             esac
   508             ;;
   509     esac
   510     CT_DoLog DEBUG "==> LD_LIBRARY_PATH='${LD_LIBRARY_PATH}'"
   511     export LD_LIBRARY_PATH
   512 }
   513 
   514 # Build up the list of allowed tarball extensions
   515 # Add them in the prefered order; most preferred comes first
   516 CT_DoListTarballExt() {
   517     if [ "${CT_CONFIGURE_has_xz}" = "y" ]; then
   518         printf ".tar.xz\n"
   519     fi
   520     if [    "${CT_CONFIGURE_has_lzma}" = "y"    \
   521          -o "${CT_CONFIGURE_has_xz}" = "y" ]; then
   522         printf ".tar.lzma\n"
   523     fi
   524     printf ".tar.bz2\n"
   525     printf ".tar.gz\n.tgz\n"
   526     printf ".tar\n"
   527     printf ".zip\n"
   528 }
   529 
   530 # Get the file name extension of a component
   531 # Usage: CT_GetFileExtension <component_name-component_version> [extension]
   532 # If found, echoes the extension to stdout, and return 0
   533 # If not found, echoes nothing on stdout, and return !0.
   534 CT_GetFileExtension() {
   535     local ext
   536     local file="$1"
   537     shift
   538     local first_ext="$1"
   539 
   540     # we need to also check for an empty extension for those very
   541     # peculiar components that don't have one (such as sstrip from
   542     # buildroot).
   543     for ext in ${first_ext} $(CT_DoListTarballExt) /.git ''; do
   544         if [ -e "${CT_TARBALLS_DIR}/${file}${ext}" ]; then
   545             echo "${ext}"
   546             exit 0
   547         fi
   548     done
   549 
   550     exit 1
   551 }
   552 
   553 # Try to retrieve the specified URL (HTTP or FTP)
   554 # Usage: CT_DoGetFile <URL>
   555 # This functions always returns true (0), as it can be legitimate not
   556 # to find the requested URL (think about snapshots, different layouts
   557 # for different gcc versions, etc...).
   558 CT_DoGetFile() {
   559     local url="${1}"
   560     local dest="${CT_TARBALLS_DIR}/${url##*/}"
   561     local tmp="${dest}.tmp-dl"
   562 
   563     # Remove potential left-over from a previous run
   564     rm -f "${tmp}"
   565 
   566     # We also retry a few times, in case there is a transient error (eg. behind
   567     # a dynamic IP that changes during the transfer...)
   568     # With automated download as we are doing, it can be very dangerous to
   569     # continue the downloads. It's far better to simply overwrite the
   570     # destination file.
   571     # Some company networks have firewalls to connect to the internet, but it's
   572     # not easy to detect them, so force a global ${CT_CONNECT_TIMEOUT}-second
   573     # timeout.
   574     if [ ${CT_CONNECT_TIMEOUT} = -1 ]; then
   575         T=
   576     else
   577         T="-T ${CT_CONNECT_TIMEOUT}"
   578     fi
   579     if CT_DoExecLog ALL wget --passive-ftp --tries=3 -nc    \
   580                              --progress=dot:binary          \
   581                              ${T}                           \
   582                              -O "${tmp}"                    \
   583                              "${url}"
   584     then
   585         # Success, we got it, good!
   586         mv "${tmp}" "${dest}"
   587         CT_DoLog DEBUG "Got it from: \"${url}\""
   588     else
   589         # Woops...
   590         rm -f "${tmp}"
   591         CT_DoLog DEBUG "Not at this location: \"${url}\""
   592     fi
   593 }
   594 
   595 # This function tries to retrieve a tarball form a local directory
   596 # Usage: CT_GetLocal <basename> [.extension]
   597 CT_GetLocal() {
   598     local basename="$1"
   599     local first_ext="$2"
   600     local ext
   601 
   602     # Do we already have it in *our* tarballs dir?
   603     if ext="$( CT_GetFileExtension "${basename}" ${first_ext} )"; then
   604         CT_DoLog DEBUG "Already have '${basename}'"
   605         return 0
   606     fi
   607 
   608     if [ -n "${CT_LOCAL_TARBALLS_DIR}" ]; then
   609         CT_DoLog DEBUG "Trying to retrieve an already downloaded copy of '${basename}'"
   610         # We'd rather have a bzip2'ed tarball, then gzipped tarball, plain tarball,
   611         # or, as a failover, a file without extension.
   612         for ext in ${first_ext} $(CT_DoListTarballExt) ''; do
   613             CT_DoLog DEBUG "Trying '${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}'"
   614             if [ -r "${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}" -a \
   615                  "${CT_FORCE_DOWNLOAD}" != "y" ]; then
   616                 CT_DoLog DEBUG "Got '${basename}' from local storage"
   617                 CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}" "${CT_TARBALLS_DIR}/${basename}${ext}"
   618                 return 0
   619             fi
   620         done
   621     fi
   622     return 1
   623 }
   624 
   625 # This function gets the custom source from either a tarball or directory
   626 # Usage: CT_GetCustom <component> <custom_version> <custom_location>
   627 CT_GetCustom() {
   628     local custom_component="$1"
   629     local custom_version="$2"
   630     local custom_location="$3"
   631     local custom_name="${custom_component}-${custom_version}"
   632 
   633     CT_TestAndAbort "${custom_name}: CT_CUSTOM_LOCATION_ROOT_DIR or ${custom_component}'s CUSTOM_LOCATION must be set." \
   634                     -z "${CT_CUSTOM_LOCATION_ROOT_DIR}" -a -z "${custom_location}"
   635 
   636     if [ -n "${CT_CUSTOM_LOCATION_ROOT_DIR}" \
   637          -a -z "${custom_location}"          ]; then
   638         custom_location="${CT_CUSTOM_LOCATION_ROOT_DIR}/${custom_component}"
   639     fi
   640 
   641     CT_DoLog EXTRA "Using '${custom_name}' from custom location"
   642     if [ ! -d "${custom_location}" ]; then
   643         # We need to know the custom tarball extension,
   644         # so we can create a properly-named symlink, which
   645         # we use later on in 'extract'
   646         case "${custom_location}" in
   647             *.tar.xz)       custom_name="${custom_name}.tar.xz";;
   648             *.tar.bz2)      custom_name="${custom_name}.tar.bz2";;
   649             *.tar.gz|*.tgz) custom_name="${custom_name}.tar.gz";;
   650             *.tar)          custom_name="${custom_name}.tar";;
   651             *)  CT_Abort "Unknown extension for custom tarball '${custom_location}'";;
   652         esac
   653         CT_DoExecLog DEBUG ln -sf "${custom_location}"  \
   654                                   "${CT_TARBALLS_DIR}/${custom_name}"
   655     else
   656         CT_DoExecLog DEBUG ln -snf "${custom_location}" \
   657                                    "${CT_SRC_DIR}/${custom_name}"
   658     fi
   659 }
   660 
   661 # This function saves the specified to local storage if possible,
   662 # and if so, symlinks it for later usage
   663 # Usage: CT_SaveLocal </full/path/file.name>
   664 CT_SaveLocal() {
   665     local file="$1"
   666     local basename="${file##*/}"
   667 
   668     if [ "${CT_SAVE_TARBALLS}" = "y" ]; then
   669         CT_DoLog EXTRA "Saving '${basename}' to local storage"
   670         # The file may already exist if downloads are forced: remove it first
   671         CT_DoExecLog ALL rm -f "${CT_LOCAL_TARBALLS_DIR}/${basename}"
   672         CT_DoExecLog ALL mv -f "${file}" "${CT_LOCAL_TARBALLS_DIR}"
   673         CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${basename}" "${file}"
   674     fi
   675 }
   676 
   677 # Download the file from one of the URLs passed as argument
   678 # Usage: CT_GetFile <basename> [.extension] <url> [url ...]
   679 CT_GetFile() {
   680     local ext
   681     local -a URLS
   682     local url
   683     local file="$1"
   684     local first_ext
   685     shift
   686     # If next argument starts with a dot, then this is not an URL,
   687     # and we can consider that it is a preferred extension.
   688     case "$1" in
   689         .*) first_ext="$1"
   690             shift
   691             ;;
   692     esac
   693 
   694     # Does it exist localy?
   695     if CT_GetLocal "${file}" ${first_ext}; then
   696         return 0
   697     fi
   698     # No, it does not...
   699 
   700     # If not allowed to download from the Internet, don't
   701     if [ "${CT_FORBID_DOWNLOAD}" = "y" ]; then
   702         CT_DoLog DEBUG "Not allowed to download from the Internet, aborting ${file} download"
   703         return 1
   704     fi
   705 
   706     # Try to retrieve the file
   707     CT_DoLog EXTRA "Retrieving '${file}'"
   708 
   709     # Add URLs on the LAN mirror
   710     if [ "${CT_USE_MIRROR}" = "y" ]; then
   711         CT_TestOrAbort "Please set the mirror base URL" -n "${CT_MIRROR_BASE_URL}"
   712         URLS+=( "${CT_MIRROR_BASE_URL}/${file%-*}" )
   713         URLS+=( "${CT_MIRROR_BASE_URL}" )
   714     fi
   715 
   716     if [ "${CT_FORCE_MIRROR}" != "y" ]; then
   717         URLS+=( "${@}" )
   718     fi
   719 
   720     # Scan all URLs in turn, and try to grab a tarball from there
   721     # Do *not* try git trees (ext=/.git), this is handled in a specific
   722     # wrapper, below
   723     for ext in ${first_ext} $(CT_DoListTarballExt) ''; do
   724         # Try all urls in turn
   725         for url in "${URLS[@]}"; do
   726             [ -n "${url}" ] || continue
   727             CT_DoLog DEBUG "Trying '${url}/${file}${ext}'"
   728             CT_DoGetFile "${url}/${file}${ext}"
   729             if [ -f "${CT_TARBALLS_DIR}/${file}${ext}" ]; then
   730                 CT_DoLog DEBUG "Got '${file}' from the Internet"
   731                 CT_SaveLocal "${CT_TARBALLS_DIR}/${file}${ext}"
   732                 return 0
   733             fi
   734         done
   735     done
   736 
   737     # Just return error, someone may want to catch and handle the error
   738     # (eg. glibc/eglibc add-ons can be missing).
   739     return 1
   740 }
   741 
   742 # Checkout from CVS, and build the associated tarball
   743 # The tarball will be called ${basename}.tar.bz2
   744 # Prerequisite: either the server does not require password,
   745 # or the user must already be logged in.
   746 # 'tag' is the tag to retrieve. Must be specified, but can be empty.
   747 # If dirname is specified, then module will be renamed to dirname
   748 # prior to building the tarball.
   749 # Usage: CT_GetCVS <basename> <url> <module> <tag> [dirname[=subdir]]
   750 # Note: if '=subdir' is given, then it is used instead of 'module'.
   751 CT_GetCVS() {
   752     local basename="$1"
   753     local uri="$2"
   754     local module="$3"
   755     local tag="${4:+-r ${4}}"
   756     local dirname="$5"
   757     local tmp_dir
   758 
   759     # First try locally, then the mirror
   760     if CT_GetFile "${basename}"; then
   761         # Got it! Return early! :-)
   762         return 0
   763     fi
   764 
   765     if [ "${CT_FORBID_DOWNLOAD}" = "y" ]; then
   766         CT_DoLog WARN "Downloads forbidden, not trying cvs retrieval"
   767         return 1
   768     fi
   769 
   770     CT_MktempDir tmp_dir
   771     CT_Pushd "${tmp_dir}"
   772 
   773     CT_DoExecLog ALL cvs -z 9 -d "${uri}" co -P ${tag} "${module}"
   774     if [ -n "${dirname}" ]; then
   775         case "${dirname}" in
   776             *=*)
   777                 CT_DoExecLog DEBUG mv "${dirname#*=}" "${dirname%%=*}"
   778                 CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${dirname%%=*}"
   779                 ;;
   780             *)
   781                 CT_DoExecLog ALL mv "${module}" "${dirname}"
   782                 CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${dirname:-${module}}"
   783                 ;;
   784         esac
   785     fi
   786     CT_SaveLocal "${CT_TARBALLS_DIR}/${basename}.tar.bz2"
   787 
   788     CT_Popd
   789     CT_DoExecLog ALL rm -rf "${tmp_dir}"
   790 }
   791 
   792 # Check out from SVN, and build the associated tarball
   793 # The tarball will be called ${basename}.tar.bz2
   794 # Prerequisite: either the server does not require password,
   795 # or the user must already be logged in.
   796 # 'rev' is the revision to retrieve
   797 # Usage: CT_GetSVN <basename> <url> [rev]
   798 CT_GetSVN() {
   799     local basename="$1"
   800     local uri="$2"
   801     local rev="$3"
   802 
   803     # First try locally, then the mirror
   804     if CT_GetFile "${basename}"; then
   805         # Got it! Return early! :-)
   806         return 0
   807     fi
   808 
   809     if [ "${CT_FORBID_DOWNLOAD}" = "y" ]; then
   810         CT_DoLog WARN "Downloads forbidden, not trying svn retrieval"
   811         return 1
   812     fi
   813 
   814     CT_MktempDir tmp_dir
   815     CT_Pushd "${tmp_dir}"
   816 
   817     if ! CT_DoExecLog ALL svn export ${rev:+-r ${rev}} "${uri}" "${basename}"; then
   818         CT_DoLog WARN "Could not retrieve '${basename}'"
   819         return 1
   820     fi
   821     CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${basename}"
   822     CT_SaveLocal "${CT_TARBALLS_DIR}/${basename}.tar.bz2"
   823 
   824     CT_Popd
   825     CT_DoExecLog ALL rm -rf "${tmp_dir}"
   826 }
   827 
   828 # Clone a git tree
   829 # Tries the given URLs in turn until one can get cloned. No tarball will be created.
   830 # Prerequisites: either the server does not require password,
   831 # or the user has already taken any action to authenticate to the server.
   832 # The cloned tree will *not* be stored in the local tarballs dir!
   833 # Usage: CT_GetGit <basename> <cset> <url>
   834 CT_GetGit() {
   835     local basename="${1}"
   836     local cset="${2}"
   837     local url="${3}"
   838     local file="${basename}-${cset}.tar.gz"
   839     local dir="${CT_TARBALLS_DIR}/${basename}-${cset}.git"
   840     local dest="${CT_TARBALLS_DIR}/${file}"
   841     local tmp="${CT_TARBALLS_DIR}/${file}.tmp-dl"
   842 
   843     # Do we alreadyhave it?
   844     if CT_GetLocal "${file}"; then
   845         return 0
   846     fi
   847     # Nope...
   848 
   849     if [ "${CT_FORBID_DOWNLOAD}" = "y" ]; then
   850         CT_DoLog WARN "Downloads forbidden, not trying git retrieval"
   851         return 1
   852     fi
   853 
   854     # Add URLs on the LAN mirror
   855     # We subvert the normal download method, just to look for
   856     # looking at the local mirror
   857     if CT_GetFile "${basename}-${cset}" .tar.gz; then
   858         return 0
   859     fi
   860 
   861     CT_DoLog EXTRA "Retrieving '${basename}-${cset}' (git)"
   862 
   863     # Remove potential left-over from a previous run
   864     CT_DoExecLog ALL rm -rf "${tmp}.tar.gz" "${tmp}.tar" "${tmp}" "${dir}"
   865 
   866     if CT_DoExecLog ALL git clone "${url}" "${dir}"; then
   867         # Yep, cloned OK
   868         CT_Pushd "${dir}"
   869         CT_DoExecLog ALL git archive --format=tar                       \
   870                                      --prefix="${basename}-${cset}/"    \
   871                                      -o "${tmp}.tar"                    \
   872                                      "${cset}"
   873         CT_DoExecLog ALL gzip -9 "${tmp}.tar"
   874         CT_DoExecLog ALL mv -f "${tmp}.tar.gz" "${dest}"
   875         CT_SaveLocal "${dest}"
   876         CT_DoExecLog ALL rm -rf "${tmp}.tar.gz" "${tmp}.tar" "${tmp}" "${dir}"
   877         CT_Popd
   878     else
   879         # Woops...
   880         CT_DoExecLog ALL rm -rf "${dir}"
   881         CT_DoLog Debug "Could not clone '${basename}'"
   882         return 1
   883     fi
   884 }
   885 
   886 # Extract a tarball
   887 # Some tarballs need to be extracted in specific places. Eg.: glibc addons
   888 # must be extracted in the glibc directory; uCLibc locales must be extracted
   889 # in the extra/locale sub-directory of uClibc. This is taken into account
   890 # by the caller, that did a 'cd' into the correct path before calling us
   891 # and sets nochdir to 'nochdir'.
   892 # Note also that this function handles the git trees!
   893 # Usage: CT_Extract [nochdir] <basename> [options]
   894 # where 'options' are dependent on the source (eg. git branch/tag...)
   895 CT_Extract() {
   896     local nochdir="$1"
   897     local basename
   898     local ext
   899     local lzma_prog
   900     local -a tar_opts
   901 
   902     if [ "${nochdir}" = "nochdir" ]; then
   903         shift
   904         nochdir="$(pwd)"
   905     else
   906         nochdir="${CT_SRC_DIR}"
   907     fi
   908 
   909     basename="$1"
   910     shift
   911 
   912     if ! ext="$(CT_GetFileExtension "${basename}")"; then
   913         CT_DoLog WARN "'${basename}' not found in '${CT_TARBALLS_DIR}'"
   914         return 1
   915     fi
   916     local full_file="${CT_TARBALLS_DIR}/${basename}${ext}"
   917 
   918     # Check if already extracted
   919     if [ -e "${CT_SRC_DIR}/.${basename}.extracted" ]; then
   920         CT_DoLog DEBUG "Already extracted '${basename}'"
   921         return 0
   922     fi
   923 
   924     # Check if previously partially extracted
   925     if [ -e "${CT_SRC_DIR}/.${basename}.extracting" ]; then
   926         CT_DoLog ERROR "The '${basename}' sources were partially extracted."
   927         CT_DoLog ERROR "Please remove first:"
   928         CT_DoLog ERROR " - the source dir for '${basename}', in '${CT_SRC_DIR}'"
   929         CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${basename}.extracting'"
   930         CT_Abort "I'll stop now to avoid any carnage..."
   931     fi
   932     CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${basename}.extracting"
   933 
   934     CT_Pushd "${nochdir}"
   935 
   936     CT_DoLog EXTRA "Extracting '${basename}'"
   937     CT_DoExecLog FILE mkdir -p "${basename}"
   938     tar_opts=( "--strip-components=1" )
   939     tar_opts+=( "-C" "${basename}" )
   940     tar_opts+=( "-xv" )
   941 
   942     # One note here:
   943     # - lzma can be handled either with 'xz' or 'lzma'
   944     # - we get lzma tarball only if either or both are available
   945     # - so, if we get an lzma tarball, and either 'xz' or 'lzma' is
   946     #   missing, we can assume the other is available
   947     if [ "${CT_CONFIGURE_has_lzma}" = "y" ]; then
   948         lzma_prog="lzma -fdc"
   949     else
   950         lzma_prog="xz -fdc"
   951     fi
   952     case "${ext}" in
   953         .tar.xz)      xz -fdc "${full_file}" | CT_DoExecLog FILE tar "${tar_opts[@]}" -f -;;
   954         .tar.lzma)    ${lzma_prog} "${full_file}" | CT_DoExecLog FILE tar "${tar_opts[@]}" -f -;;
   955         .tar.bz2)     bzip2 -dc "${full_file}" | CT_DoExecLog FILE tar "${tar_opts[@]}" -f -;;
   956         .tar.gz|.tgz) gzip -dc "${full_file}" | CT_DoExecLog FILE tar "${tar_opts[@]}" -f -;;
   957         .tar)         CT_DoExecLog FILE tar "${tar_opts[@]}" -f "${full_file}";;
   958         .zip)         CT_DoExecLog FILE unzip "${@}" "${full_file}";;
   959         /.git)        CT_ExtractGit "${basename}" "${@}";;
   960         *)            CT_DoLog WARN "Don't know how to handle '${basename}${ext}': unknown extension"
   961                       return 1
   962                       ;;
   963     esac
   964 
   965     # Don't mark as being extracted for git
   966     case "${ext}" in
   967         /.git)  ;;
   968         *)      CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${basename}.extracted";;
   969     esac
   970     CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/.${basename}.extracting"
   971 
   972     CT_Popd
   973 }
   974 
   975 # Create a working git clone of a local git repository
   976 # Usage: CT_ExtractGit <basename> [ref]
   977 # where 'ref' is the reference to use:
   978 #   the full name of a branch, like "remotes/origin/branch_name"
   979 #   a date as understandable by git, like "YYYY-MM-DD[ hh[:mm[:ss]]]"
   980 #   a tag name
   981 # If 'ref' is not given, the current repository HEAD will be used
   982 CT_ExtractGit() {
   983     local basename="${1}"
   984     local ref="${2}"
   985     local repo
   986     local ref_type
   987 
   988     # pushd now to be able to get git revlist in case ref is a date
   989     repo="${CT_TARBALLS_DIR}/${basename}"
   990     CT_Pushd "${repo}"
   991 
   992     # What kind of reference is ${ref} ?
   993     if [ -z "${ref}" ]; then
   994         ref_type=head
   995         ref=$(git rev-list -n1 HEAD)
   996     elif git tag |grep -E "^${ref}$" >/dev/null 2>&1; then
   997         ref_type=tag
   998     elif git branch -a --no-color |grep -E "^. ${ref}$" >/dev/null 2>&1; then
   999         ref_type=branch
  1000     elif date -d "${ref}" >/dev/null 2>&1; then
  1001         ref_type=date
  1002         ref=$(git rev-list -n1 --before="${ref}")
  1003     else
  1004         CT_Abort "Reference '${ref}' is an incorrect git reference: neither tag, branch nor date"
  1005     fi
  1006 
  1007     CT_Popd
  1008 
  1009     CT_DoExecLog FILE rmdir "${basename}"
  1010     case "${ref_type}" in
  1011         branch) CT_DoExecLog FILE git clone -b "${ref}" "${repo}" "${basename}" ;;
  1012         *)      CT_DoExecLog FILE git clone "${repo}" "${basename}"
  1013                 CT_Pushd "${basename}"
  1014                 CT_DoExecLog FILE git checkout "${ref}"
  1015                 CT_Popd
  1016                 ;;
  1017     esac
  1018 }
  1019 
  1020 # Patches the specified component
  1021 # See CT_Extract, above, for explanations on 'nochdir'
  1022 # Usage: CT_Patch [nochdir] <packagename> <packageversion>
  1023 # If the package directory is *not* packagename-packageversion, then
  1024 # the caller must cd into the proper directory first, and call us
  1025 # with nochdir
  1026 CT_Patch() {
  1027     local nochdir="$1"
  1028     local pkgname
  1029     local version
  1030     local pkgdir
  1031     local base_file
  1032     local ver_file
  1033     local d
  1034     local -a patch_dirs
  1035     local bundled_patch_dir
  1036     local local_patch_dir
  1037 
  1038     if [ "${nochdir}" = "nochdir" ]; then
  1039         shift
  1040         pkgname="$1"
  1041         version="$2"
  1042         pkgdir="${pkgname}-${version}"
  1043         nochdir="$(pwd)"
  1044     else
  1045         pkgname="$1"
  1046         version="$2"
  1047         pkgdir="${pkgname}-${version}"
  1048         nochdir="${CT_SRC_DIR}/${pkgdir}"
  1049     fi
  1050 
  1051     # Check if already patched
  1052     if [ -e "${CT_SRC_DIR}/.${pkgdir}.patched" ]; then
  1053         CT_DoLog DEBUG "Already patched '${pkgdir}'"
  1054         return 0
  1055     fi
  1056 
  1057     # Check if already partially patched
  1058     if [ -e "${CT_SRC_DIR}/.${pkgdir}.patching" ]; then
  1059         CT_DoLog ERROR "The '${pkgdir}' sources were partially patched."
  1060         CT_DoLog ERROR "Please remove first:"
  1061         CT_DoLog ERROR " - the source dir for '${pkgdir}', in '${CT_SRC_DIR}'"
  1062         CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${pkgdir}.extracted'"
  1063         CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${pkgdir}.patching'"
  1064         CT_Abort "I'll stop now to avoid any carnage..."
  1065     fi
  1066     touch "${CT_SRC_DIR}/.${pkgdir}.patching"
  1067 
  1068     CT_Pushd "${nochdir}"
  1069 
  1070     CT_DoLog EXTRA "Patching '${pkgdir}'"
  1071 
  1072     bundled_patch_dir="${CT_LIB_DIR}/patches/${pkgname}/${version}"
  1073     local_patch_dir="${CT_LOCAL_PATCH_DIR}/${pkgname}/${version}"
  1074 
  1075     case "${CT_PATCH_ORDER}" in
  1076         bundled)        patch_dirs=("${bundled_patch_dir}");;
  1077         local)          patch_dirs=("${local_patch_dir}");;
  1078         bundled,local)  patch_dirs=("${bundled_patch_dir}" "${local_patch_dir}");;
  1079         local,bundled)  patch_dirs=("${local_patch_dir}" "${bundled_patch_dir}");;
  1080         none)           patch_dirs=;;
  1081     esac
  1082 
  1083     for d in "${patch_dirs[@]}"; do
  1084         CT_DoLog DEBUG "Looking for patches in '${d}'..."
  1085         if [ -n "${d}" -a -d "${d}" ]; then
  1086             for p in "${d}"/*.patch; do
  1087                 if [ -f "${p}" ]; then
  1088                     CT_DoExecLog ALL patch --no-backup-if-mismatch -g0 -F1 -p1 -f -i "${p}"
  1089                 fi
  1090             done
  1091             if [ "${CT_PATCH_SINGLE}" = "y" ]; then
  1092                 break
  1093             fi
  1094         fi
  1095     done
  1096 
  1097     if [ "${CT_OVERIDE_CONFIG_GUESS_SUB}" = "y" ]; then
  1098         CT_DoLog ALL "Overiding config.guess and config.sub"
  1099         for cfg in config_guess config_sub; do
  1100             eval ${cfg}="${CT_LIB_DIR}/scripts/${cfg/_/.}"
  1101             [ -e "${CT_TOP_DIR}/scripts/${cfg/_/.}" ] && eval ${cfg}="${CT_TOP_DIR}/scripts/${cfg/_/.}"
  1102             # Can't use CT_DoExecLog because of the '{} \;' to be passed un-mangled to find
  1103             find . -type f -name "${cfg/_/.}" -exec cp -v "${!cfg}" {} \; |CT_DoLog ALL
  1104         done
  1105     fi
  1106 
  1107     CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${pkgdir}.patched"
  1108     CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/.${pkgdir}.patching"
  1109 
  1110     CT_Popd
  1111 }
  1112 
  1113 # Two wrappers to call config.(guess|sub) either from CT_TOP_DIR or CT_LIB_DIR.
  1114 # Those from CT_TOP_DIR, if they exist, will be be more recent than those from CT_LIB_DIR.
  1115 CT_DoConfigGuess() {
  1116     if [ -x "${CT_TOP_DIR}/scripts/config.guess" ]; then
  1117         "${CT_TOP_DIR}/scripts/config.guess"
  1118     else
  1119         "${CT_LIB_DIR}/scripts/config.guess"
  1120     fi
  1121 }
  1122 
  1123 CT_DoConfigSub() {
  1124     if [ -x "${CT_TOP_DIR}/scripts/config.sub" ]; then
  1125         "${CT_TOP_DIR}/scripts/config.sub" "$@"
  1126     else
  1127         "${CT_LIB_DIR}/scripts/config.sub" "$@"
  1128     fi
  1129 }
  1130 
  1131 # Compute the target tuple from what is provided by the user
  1132 # Usage: CT_DoBuildTargetTuple
  1133 # In fact this function takes the environment variables to build the target
  1134 # tuple. It is needed both by the normal build sequence, as well as the
  1135 # sample saving sequence.
  1136 CT_DoBuildTargetTuple() {
  1137     # Set the endianness suffix, and the default endianness gcc option
  1138     case "${CT_ARCH_ENDIAN}" in
  1139         big)
  1140             target_endian_eb=eb
  1141             target_endian_be=be
  1142             target_endian_el=
  1143             target_endian_le=
  1144             CT_ARCH_ENDIAN_CFLAG="-mbig-endian"
  1145             CT_ARCH_ENDIAN_LDFLAG="-Wl,-EB"
  1146             ;;
  1147         little)
  1148             target_endian_eb=
  1149             target_endian_be=
  1150             target_endian_el=el
  1151             target_endian_le=le
  1152             CT_ARCH_ENDIAN_CFLAG="-mlittle-endian"
  1153             CT_ARCH_ENDIAN_LDFLAG="-Wl,-EL"
  1154             ;;
  1155     esac
  1156 
  1157     # Build the default architecture tuple part
  1158     CT_TARGET_ARCH="${CT_ARCH}${CT_ARCH_SUFFIX}"
  1159 
  1160     # Set defaults for the system part of the tuple. Can be overriden
  1161     # by architecture-specific values.
  1162     case "${CT_LIBC}" in
  1163         *glibc) CT_TARGET_SYS=gnu;;
  1164         uClibc) CT_TARGET_SYS=uclibc;;
  1165         *)      CT_TARGET_SYS=elf;;
  1166     esac
  1167 
  1168     # Set the default values for ARCH, ABI, CPU, TUNE, FPU and FLOAT
  1169     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
  1170     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
  1171     [ "${CT_ARCH_ARCH}"     ] && { CT_ARCH_ARCH_CFLAG="-march=${CT_ARCH_ARCH}";  CT_ARCH_WITH_ARCH="--with-arch=${CT_ARCH_ARCH}"; }
  1172     [ "${CT_ARCH_ABI}"      ] && { CT_ARCH_ABI_CFLAG="-mabi=${CT_ARCH_ABI}";     CT_ARCH_WITH_ABI="--with-abi=${CT_ARCH_ABI}";    }
  1173     [ "${CT_ARCH_CPU}"      ] && { CT_ARCH_CPU_CFLAG="-mcpu=${CT_ARCH_CPU}";     CT_ARCH_WITH_CPU="--with-cpu=${CT_ARCH_CPU}";    }
  1174     [ "${CT_ARCH_TUNE}"     ] && { CT_ARCH_TUNE_CFLAG="-mtune=${CT_ARCH_TUNE}";  CT_ARCH_WITH_TUNE="--with-tune=${CT_ARCH_TUNE}"; }
  1175     [ "${CT_ARCH_FPU}"      ] && { CT_ARCH_FPU_CFLAG="-mfpu=${CT_ARCH_FPU}";     CT_ARCH_WITH_FPU="--with-fpu=${CT_ARCH_FPU}";    }
  1176 
  1177     case "${CT_ARCH_FLOAT}" in
  1178         hard)
  1179             CT_ARCH_FLOAT_CFLAG="-mhard-float"
  1180             CT_ARCH_WITH_FLOAT="--with-float=hard"
  1181             ;;
  1182         soft)
  1183             CT_ARCH_FLOAT_CFLAG="-msoft-float"
  1184             CT_ARCH_WITH_FLOAT="--with-float=soft"
  1185             ;;
  1186         softfp)
  1187             CT_ARCH_FLOAT_CFLAG="-mfloat-abi=softfp"
  1188             CT_ARCH_WITH_FLOAT="--with-float=softfp"
  1189             ;;
  1190     esac
  1191 
  1192     # Build the default kernel tuple part
  1193     CT_TARGET_KERNEL="${CT_KERNEL}"
  1194 
  1195     # Overide the default values with the components specific settings
  1196     CT_DoArchTupleValues
  1197     CT_DoKernelTupleValues
  1198 
  1199     # Finish the target tuple construction
  1200     CT_TARGET="${CT_TARGET_ARCH}"
  1201     CT_TARGET="${CT_TARGET}${CT_TARGET_VENDOR:+-${CT_TARGET_VENDOR}}"
  1202     CT_TARGET="${CT_TARGET}${CT_TARGET_KERNEL:+-${CT_TARGET_KERNEL}}"
  1203     CT_TARGET="${CT_TARGET}${CT_TARGET_SYS:+-${CT_TARGET_SYS}}"
  1204 
  1205     # Sanity checks
  1206     __sed_alias=""
  1207     if [ -n "${CT_TARGET_ALIAS_SED_EXPR}" ]; then
  1208         __sed_alias=$(echo "${CT_TARGET}" |sed -r -e "${CT_TARGET_ALIAS_SED_EXPR}")
  1209     fi
  1210     case ":${CT_TARGET_VENDOR}:${CT_TARGET_ALIAS}:${__sed_alias}:" in
  1211       :*" "*:*:*:) CT_Abort "Don't use spaces in the vendor string, it breaks things.";;
  1212       :*"-"*:*:*:) CT_Abort "Don't use dashes in the vendor string, it breaks things.";;
  1213       :*:*" "*:*:) CT_Abort "Don't use spaces in the target alias, it breaks things.";;
  1214       :*:*:*" "*:) CT_Abort "Don't use spaces in the target sed transform, it breaks things.";;
  1215     esac
  1216 
  1217     # Canonicalise it
  1218     CT_TARGET=$(CT_DoConfigSub "${CT_TARGET}")
  1219     # Prepare the target CFLAGS
  1220     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ENDIAN_CFLAG}"
  1221     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ARCH_CFLAG}"
  1222     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ABI_CFLAG}"
  1223     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_CPU_CFLAG}"
  1224     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_TUNE_CFLAG}"
  1225     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FPU_CFLAG}"
  1226     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FLOAT_CFLAG}"
  1227 
  1228     # Now on for the target LDFLAGS
  1229     CT_ARCH_TARGET_LDFLAGS="${CT_ARCH_TARGET_LDFLAGS} ${CT_ARCH_ENDIAN_LDFLAG}"
  1230 }
  1231 
  1232 # This function does pause the build until the user strikes "Return"
  1233 # Usage: CT_DoPause [optional_message]
  1234 CT_DoPause() {
  1235     local foo
  1236     local message="${1:-Pausing for your pleasure}"
  1237     CT_DoLog INFO "${message}"
  1238     read -p "Press 'Enter' to continue, or Ctrl-C to stop..." foo >&6
  1239     return 0
  1240 }
  1241 
  1242 # This function creates a tarball of the specified directory, but
  1243 # only if it exists
  1244 # Usage: CT_DoTarballIfExists <dir> <tarball_basename> [extra_tar_options [...]]
  1245 CT_DoTarballIfExists() {
  1246     local dir="$1"
  1247     local tarball="$2"
  1248     shift 2
  1249     local -a extra_tar_opts=( "$@" )
  1250     local -a compress
  1251 
  1252     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
  1253         y)  compress=( gzip -c -3 - ); tar_ext=.gz;;
  1254         *)  compress=( cat - );        tar_ext=;;
  1255     esac
  1256 
  1257     if [ -d "${dir}" ]; then
  1258         CT_DoLog DEBUG "  Saving '${dir}'"
  1259         { tar c -C "${dir}" -v -f - "${extra_tar_opts[@]}" .    \
  1260           |"${compress[@]}" >"${tarball}.tar${tar_ext}"         ;
  1261         } 2>&1 |sed -r -e 's/^/    /;' |CT_DoLog STATE
  1262     else
  1263         CT_DoLog STATE "  Not saving '${dir}': does not exist"
  1264     fi
  1265 }
  1266 
  1267 # This function extracts a tarball to the specified directory, but
  1268 # only if the tarball exists
  1269 # Usage: CT_DoExtractTarballIfExists <tarball_basename> <dir> [extra_tar_options [...]]
  1270 CT_DoExtractTarballIfExists() {
  1271     local tarball="$1"
  1272     local dir="$2"
  1273     shift 2
  1274     local -a extra_tar_opts=( "$@" )
  1275     local -a uncompress
  1276 
  1277     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
  1278         y)  uncompress=( gzip -c -d ); tar_ext=.gz;;
  1279         *)  uncompress=( cat );        tar_ext=;;
  1280     esac
  1281 
  1282     if [ -f "${tarball}.tar${tar_ext}" ]; then
  1283         CT_DoLog DEBUG "  Restoring '${dir}'"
  1284         CT_DoForceRmdir "${dir}"
  1285         CT_DoExecLog DEBUG mkdir -p "${dir}"
  1286         { "${uncompress[@]}" "${tarball}.tar${tar_ext}"     \
  1287           |tar x -C "${dir}" -v -f - "${extra_tar_opts[@]}" ;
  1288         } 2>&1 |sed -r -e 's/^/    /;' |CT_DoLog STATE
  1289     else
  1290         CT_DoLog STATE "  Not restoring '${dir}': does not exist"
  1291     fi
  1292 }
  1293 
  1294 # This function saves the state of the toolchain to be able to restart
  1295 # at any one point
  1296 # Usage: CT_DoSaveState <next_step_name>
  1297 CT_DoSaveState() {
  1298 	[ "${CT_DEBUG_CT_SAVE_STEPS}" = "y" ] || return 0
  1299     local state_name="$1"
  1300     local state_dir="${CT_STATE_DIR}/${state_name}"
  1301 
  1302     # Log this to the log level required by the user
  1303     CT_DoLog ${CT_LOG_LEVEL_MAX} "Saving state to restart at step '${state_name}'..."
  1304 
  1305     rm -rf "${state_dir}"
  1306     mkdir -p "${state_dir}"
  1307 
  1308     CT_DoLog STATE "  Saving environment and aliases"
  1309     # We must omit shell functions, and some specific bash variables
  1310     # that break when restoring the environment, later. We could do
  1311     # all the processing in the awk script, but a sed is easier...
  1312     set |awk '
  1313               BEGIN { _p = 1; }
  1314               $0~/^[^ ]+ \(\)/ { _p = 0; }
  1315               _p == 1
  1316               $0 == "}" { _p = 1; }
  1317               ' |sed -r -e '/^BASH_(ARGC|ARGV|LINENO|SOURCE|VERSINFO)=/d;
  1318                            /^(UID|EUID)=/d;
  1319                            /^(FUNCNAME|GROUPS|PPID|SHELLOPTS)=/d;' >"${state_dir}/env.sh"
  1320 
  1321     CT_DoTarballIfExists "${CT_BUILDTOOLS_PREFIX_DIR}" "${state_dir}/buildtools_dir"
  1322     CT_DoTarballIfExists "${CT_CONFIG_DIR}" "${state_dir}/config_dir"
  1323     CT_DoTarballIfExists "${CT_PREFIX_DIR}" "${state_dir}/prefix_dir" --exclude '*.log'
  1324 
  1325     CT_DoLog STATE "  Saving log file"
  1326     exec >/dev/null
  1327     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
  1328         y)  gzip -3 -c "${tmp_log_file}"  >"${state_dir}/log.gz";;
  1329         *)  cat "${tmp_log_file}" >"${state_dir}/log";;
  1330     esac
  1331     exec >>"${tmp_log_file}"
  1332 }
  1333 
  1334 # This function restores a previously saved state
  1335 # Usage: CT_DoLoadState <state_name>
  1336 CT_DoLoadState(){
  1337     local state_name="$1"
  1338     local state_dir="${CT_STATE_DIR}/${state_name}"
  1339     local old_RESTART="${CT_RESTART}"
  1340     local old_STOP="${CT_STOP}"
  1341 
  1342     CT_TestOrAbort "The previous build did not reach the point where it could be restarted at '${CT_RESTART}'" -d "${state_dir}"
  1343 
  1344     # We need to do something special with the log file!
  1345     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
  1346         exec >"${state_dir}/tail.log"
  1347     fi
  1348 
  1349     # Log this to the log level required by the user
  1350     CT_DoLog ${CT_LOG_LEVEL_MAX} "Restoring state at step '${state_name}', as requested."
  1351 
  1352     CT_DoExtractTarballIfExists "${state_dir}/prefix_dir" "${CT_PREFIX_DIR}"
  1353     CT_DoExtractTarballIfExists "${state_dir}/config_dir" "${CT_CONFIG_DIR}"
  1354     CT_DoExtractTarballIfExists "${state_dir}/buildtools_dir" "${CT_BUILDTOOLS_PREFIX_DIR}"
  1355 
  1356     # Restore the environment, discarding any error message
  1357     # (for example, read-only bash internals)
  1358     CT_DoLog STATE "  Restoring environment"
  1359     . "${state_dir}/env.sh" >/dev/null 2>&1 || true
  1360 
  1361     # Restore the new RESTART and STOP steps
  1362     CT_RESTART="${old_RESTART}"
  1363     CT_STOP="${old_STOP}"
  1364     unset old_stop old_restart
  1365 
  1366     CT_DoLog STATE "  Restoring log file"
  1367     exec >/dev/null
  1368     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
  1369         y)  gzip -dc "${state_dir}/log.gz" >"${tmp_log_file}";;
  1370         *)  cat "${state_dir}/log" >"${tmp_log_file}";;
  1371     esac
  1372     cat "${state_dir}/tail.log" >>"${tmp_log_file}"
  1373     exec >>"${tmp_log_file}"
  1374     rm -f "${state_dir}/tail.log"
  1375 }