scripts/functions
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Tue Sep 23 14:48:10 2008 +0000 (2008-09-23)
changeset 872 fd4bf138f08f
parent 799 a31e625eb343
child 919 70f7ea9f910a
permissions -rw-r--r--
Bart De VOS pointed out that removing absolute paths from the libc linker scripts is plainly wrong.
It dates from dawn ages of the original crosstool code, and is not well explained. At that time, binutils might not understand the sysroot stuff, and it was necessary to remove absolute paths in that case.

/trunk/scripts/build/libc/glibc.sh | 14 2 12 0 ++------------
1 file changed, 2 insertions(+), 12 deletions(-)
     1 # This file contains some usefull common functions
     2 # Copyright 2007 Yann E. MORIN
     3 # Licensed under the GPL v2. See COPYING in the root of this package
     4 
     5 # Prepare the fault handler
     6 CT_OnError() {
     7     ret=$?
     8     # Bail out early in subshell, the upper level shell will act accordingly.
     9     [ ${BASH_SUBSHELL} -eq 0 ] || exit $ret
    10     CT_DoLog ERROR "Build failed in step '${CT_STEP_MESSAGE[${CT_STEP_COUNT}]}'"
    11     for((step=(CT_STEP_COUNT-1); step>1; step--)); do
    12         CT_DoLog ERROR "      called in step '${CT_STEP_MESSAGE[${step}]}'"
    13     done
    14     CT_DoLog ERROR "Error happened in '${BASH_SOURCE[1]}' in function '${FUNCNAME[1]}' (line unknown, sorry)"
    15     for((depth=2; ${BASH_LINENO[$((${depth}-1))]}>0; depth++)); do
    16         CT_DoLog ERROR "      called from '${BASH_SOURCE[${depth}]}' at line # ${BASH_LINENO[${depth}-1]} in function '${FUNCNAME[${depth}]}'"
    17     done
    18     [ "${CT_LOG_TO_FILE}" = "y" ] && CT_DoLog ERROR "Look at '${CT_LOG_FILE}' for more info on this error."
    19     CT_STEP_COUNT=1
    20     CT_DoEnd ERROR
    21     exit $ret
    22 }
    23 
    24 # Install the fault handler
    25 trap CT_OnError ERR
    26 
    27 # Inherit the fault handler in subshells and functions
    28 set -E
    29 
    30 # Make pipes fail on the _first_ failed command
    31 # Not supported on bash < 3.x, but we need it, so drop the obsoleting bash-2.x
    32 set -o pipefail
    33 
    34 # Don't hash commands' locations, and search every time it is requested.
    35 # This is slow, but needed because of the static/shared core gcc which shall
    36 # always match to shared if it exists, and only fallback to static if the
    37 # shared is not found
    38 set +o hashall
    39 
    40 # Log policy:
    41 #  - first of all, save stdout so we can see the live logs: fd #6
    42 exec 6>&1
    43 #  - then point stdout to the log file (temporary for now)
    44 tmp_log_file="${CT_TOP_DIR}/log.$$"
    45 exec >>"${tmp_log_file}"
    46 
    47 # The different log levels:
    48 CT_LOG_LEVEL_ERROR=0
    49 CT_LOG_LEVEL_WARN=1
    50 CT_LOG_LEVEL_INFO=2
    51 CT_LOG_LEVEL_EXTRA=3
    52 CT_LOG_LEVEL_DEBUG=4
    53 CT_LOG_LEVEL_ALL=5
    54 
    55 # A function to log what is happening
    56 # Different log level are available:
    57 #   - ERROR:   A serious, fatal error occurred
    58 #   - WARN:    A non fatal, non serious error occurred, take your responsbility with the generated build
    59 #   - INFO:    Informational messages
    60 #   - EXTRA:   Extra informational messages
    61 #   - DEBUG:   Debug messages
    62 #   - ALL:     Component's build messages
    63 # Usage: CT_DoLog <level> [message]
    64 # If message is empty, then stdin will be logged.
    65 CT_DoLog() {
    66     local max_level LEVEL level cur_l cur_L
    67     local l
    68     eval max_level="\${CT_LOG_LEVEL_${CT_LOG_LEVEL_MAX}}"
    69     # Set the maximum log level to DEBUG if we have none
    70     [ -z "${max_level}" ] && max_level=${CT_LOG_LEVEL_DEBUG}
    71 
    72     LEVEL="$1"; shift
    73     eval level="\${CT_LOG_LEVEL_${LEVEL}}"
    74 
    75     if [ $# -eq 0 ]; then
    76         cat -
    77     else
    78         echo "${@}"
    79     fi |( IFS="\n" # We want the full lines, even leading spaces
    80           _prog_bar_cpt=0
    81           _prog_bar[0]='/'
    82           _prog_bar[1]='-'
    83           _prog_bar[2]='\'
    84           _prog_bar[3]='|'
    85           indent=$((2*CT_STEP_COUNT))
    86           while read line; do
    87               case "${CT_LOG_SEE_TOOLS_WARN},${line}" in
    88                 y,*"warning:"*)         cur_L=WARN; cur_l=${CT_LOG_LEVEL_WARN};;
    89                 y,*"WARNING:"*)         cur_L=WARN; cur_l=${CT_LOG_LEVEL_WARN};;
    90                 *"error:"*)             cur_L=ERROR; cur_l=${CT_LOG_LEVEL_ERROR};;
    91                 *"make["?*"]:"*"Stop.") cur_L=ERROR; cur_l=${CT_LOG_LEVEL_ERROR};;
    92                 *)                      cur_L="${LEVEL}"; cur_l="${level}";;
    93               esac
    94               # There will always be a log file (stdout, fd #1), be it /dev/null
    95               printf "[%-5s]%*s%s%s\n" "${cur_L}" "${indent}" " " "${line}"
    96               if [ ${cur_l} -le ${max_level} ]; then
    97                   # Only print to console (fd #6) if log level is high enough.
    98                   printf "\r[%-5s]%*s%s%s\n" "${cur_L}" "${indent}" " " "${line}" >&6
    99               fi
   100               if [ "${CT_LOG_PROGRESS_BAR}" = "y" ]; then
   101                   printf "\r[%02d:%02d] %s " $((SECONDS/60)) $((SECONDS%60)) "${_prog_bar[$((_prog_bar_cpt/10))]}" >&6
   102                   _prog_bar_cpt=$(((_prog_bar_cpt+1)%40))
   103               fi
   104           done
   105         )
   106 
   107     return 0
   108 }
   109 
   110 # Execute an action, and log its messages
   111 # Usage: CT_DoExecLog <level> <[VAR=val...] command [parameters...]>
   112 CT_DoExecLog() {
   113     local level="$1"
   114     shift
   115     CT_DoLog DEBUG "==> Executing: '${@}'"
   116     "${@}" 2>&1 |CT_DoLog "${level}"
   117 }
   118 
   119 # Tail message to be logged whatever happens
   120 # Usage: CT_DoEnd <level>
   121 CT_DoEnd()
   122 {
   123     local level="$1"
   124     CT_STOP_DATE=$(CT_DoDate +%s%N)
   125     CT_STOP_DATE_HUMAN=$(CT_DoDate +%Y%m%d.%H%M%S)
   126     if [ "${level}" != "ERROR" ]; then
   127         CT_DoLog "${level:-INFO}" "Build completed at ${CT_STOP_DATE_HUMAN}"
   128     fi
   129     elapsed=$((CT_STOP_DATE-CT_STAR_DATE))
   130     elapsed_min=$((elapsed/(60*1000*1000*1000)))
   131     elapsed_sec=$(printf "%02d" $(((elapsed%(60*1000*1000*1000))/(1000*1000*1000))))
   132     elapsed_csec=$(printf "%02d" $(((elapsed%(1000*1000*1000))/(10*1000*1000))))
   133     CT_DoLog ${level:-INFO} "(elapsed: ${elapsed_min}:${elapsed_sec}.${elapsed_csec})"
   134 }
   135 
   136 # Abort the execution with an error message
   137 # Usage: CT_Abort <message>
   138 CT_Abort() {
   139     CT_DoLog ERROR "$1"
   140     exit 1
   141 }
   142 
   143 # Test a condition, and print a message if satisfied
   144 # Usage: CT_Test <message> <tests>
   145 CT_Test() {
   146     local ret
   147     local m="$1"
   148     shift
   149     test "$@" && CT_DoLog WARN "$m"
   150     return 0
   151 }
   152 
   153 # Test a condition, and abort with an error message if satisfied
   154 # Usage: CT_TestAndAbort <message> <tests>
   155 CT_TestAndAbort() {
   156     local m="$1"
   157     shift
   158     test "$@" && CT_Abort "$m"
   159     return 0
   160 }
   161 
   162 # Test a condition, and abort with an error message if not satisfied
   163 # Usage: CT_TestAndAbort <message> <tests>
   164 CT_TestOrAbort() {
   165     local m="$1"
   166     shift
   167     test "$@" || CT_Abort "$m"
   168     return 0
   169 }
   170 
   171 # Test the presence of a tool, or abort if not found
   172 # Usage: CT_HasOrAbort <tool>
   173 CT_HasOrAbort() {
   174     CT_TestAndAbort "'${1}' not found and needed for successful toolchain build." -z "$(CT_Which "${1}")"
   175     return 0
   176 }
   177 
   178 # Search a program: wrap "which" for those system where
   179 # "which" verbosely says there is no match (Mdk are such
   180 # suckers...)
   181 # Usage: CT_Which <filename>
   182 CT_Which() {
   183   which "$1" 2>/dev/null || true
   184 }
   185 
   186 # Get current date with nanosecond precision
   187 # On those system not supporting nanosecond precision, faked with rounding down
   188 # to the highest entire second
   189 # Usage: CT_DoDate <fmt>
   190 CT_DoDate() {
   191     date "$1" |sed -r -e 's/%N$/000000000/;'
   192 }
   193 
   194 CT_STEP_COUNT=1
   195 CT_STEP_MESSAGE[${CT_STEP_COUNT}]="<none>"
   196 # Memorise a step being done so that any error is caught
   197 # Usage: CT_DoStep <loglevel> <message>
   198 CT_DoStep() {
   199     local start=$(CT_DoDate +%s%N)
   200     CT_DoLog "$1" "================================================================="
   201     CT_DoLog "$1" "$2"
   202     CT_STEP_COUNT=$((CT_STEP_COUNT+1))
   203     CT_STEP_LEVEL[${CT_STEP_COUNT}]="$1"; shift
   204     CT_STEP_START[${CT_STEP_COUNT}]="${start}"
   205     CT_STEP_MESSAGE[${CT_STEP_COUNT}]="$1"
   206     return 0
   207 }
   208 
   209 # End the step just being done
   210 # Usage: CT_EndStep
   211 CT_EndStep() {
   212     local stop=$(CT_DoDate +%s%N)
   213     local duration=$(printf "%032d" $((stop-${CT_STEP_START[${CT_STEP_COUNT}]})) |sed -r -e 's/([[:digit:]]{2})[[:digit:]]{7}$/\.\1/; s/^0+//; s/^\./0\./;')
   214     local elapsed=$(printf "%02d:%02d" $((SECONDS/60)) $((SECONDS%60)))
   215     local level="${CT_STEP_LEVEL[${CT_STEP_COUNT}]}"
   216     local message="${CT_STEP_MESSAGE[${CT_STEP_COUNT}]}"
   217     CT_STEP_COUNT=$((CT_STEP_COUNT-1))
   218     CT_DoLog "${level}" "${message}: done in ${duration}s (at ${elapsed})"
   219     return 0
   220 }
   221 
   222 # Pushes into a directory, and pops back
   223 CT_Pushd() {
   224     pushd "$1" >/dev/null 2>&1
   225 }
   226 CT_Popd() {
   227     popd >/dev/null 2>&1
   228 }
   229 
   230 # Makes a path absolute
   231 # Usage: CT_MakeAbsolutePath path
   232 CT_MakeAbsolutePath() {
   233     # Try to cd in that directory
   234     if [ -d "$1" ]; then
   235         CT_Pushd "$1"
   236         pwd
   237         CT_Popd
   238     else
   239         # No such directory, fail back to guessing
   240         case "$1" in
   241             /*)  echo "$1";;
   242             *)   echo "$(pwd)/$1";;
   243         esac
   244     fi
   245     
   246     return 0
   247 }
   248 
   249 # Creates a temporary directory
   250 # $1: variable to assign to
   251 # Usage: CT_MktempDir foo
   252 CT_MktempDir() {
   253     # Some mktemp do not allow more than 6 Xs
   254     eval "$1"=$(mktemp -q -d "${CT_BUILD_DIR}/.XXXXXX")
   255     CT_TestOrAbort "Could not make temporary directory" -n "${!1}" -a -d "${!1}"
   256     CT_DoLog DEBUG "Made temporary directory '${!1}'"
   257     return 0
   258 }
   259 
   260 # Echoes the specified string on stdout until the pipe breaks.
   261 # Doesn't fail
   262 # $1: string to echo
   263 # Usage: CT_DoYes "" |make oldconfig
   264 CT_DoYes() {
   265     yes "$1" || true
   266 }
   267 
   268 # Get the file name extension of a component
   269 # Usage: CT_GetFileExtension <component_name-component_version> [extension]
   270 # If found, echoes the extension to stdout
   271 # If not found, echoes nothing on stdout.
   272 CT_GetFileExtension() {
   273     local ext
   274     local file="$1"
   275     shift
   276     local first_ext="$1"
   277 
   278     CT_Pushd "${CT_TARBALLS_DIR}"
   279     # we need to also check for an empty extension for those very
   280     # peculiar components that don't have one (such as sstrip from
   281     # buildroot).
   282     for ext in ${first_ext} .tar.gz .tar.bz2 .tgz .tar ''; do
   283         if [ -f "${file}${ext}" ]; then
   284             echo "${ext}"
   285             break
   286         fi
   287     done
   288     CT_Popd
   289 
   290     return 0
   291 }
   292 
   293 # Set environment for proxy access
   294 # Usage: CT_DoSetProxy <proxy_type>
   295 # where proxy_type is one of 'http', 'sockssys', 'socks4' or 'socks5',
   296 # or empty (to not change proxy settings).
   297 CT_DoSetProxy() {
   298     case "${1}" in
   299         http)
   300             http_proxy="http://"
   301             case  "${CT_PROXY_USER}:${CT_PROXY_PASS}" in
   302                 :)      ;;
   303                 :*)     http_proxy="${http_proxy}:${CT_PROXY_PASS}@";;
   304                 *:)     http_proxy="${http_proxy}${CT_PROXY_USER}@";;
   305                 *:*)    http_proxy="${http_proxy}${CT_PROXY_USER}:${CT_PROXY_PASS}@";;
   306             esac
   307             export http_proxy="${http_proxy}${CT_PROXY_HOST}:${CT_PROXY_PORT}/"
   308             export https_proxy="${http_proxy}"
   309             export ftp_proxy="${http_proxy}"
   310             CT_DoLog DEBUG "http_proxy='${http_proxy}'"
   311             ;;
   312         sockssys)
   313             CT_HasOrAbort tsocks
   314             . tsocks -on
   315             ;;
   316         socks*)
   317             # Remove any lingering config file from any previous run
   318             rm -f "${CT_BUILD_DIR}/tsocks.conf"
   319             # Find all interfaces and build locally accessible networks
   320             server_ip=$(ping -c 1 -W 2 "${CT_PROXY_HOST}" |head -n 1 |sed -r -e 's/^[^\(]+\(([^\)]+)\).*$/\1/;' || true)
   321             CT_TestOrAbort "SOCKS proxy '${CT_PROXY_HOST}' has no IP." -n "${server_ip}"
   322             /sbin/ifconfig |awk -v server_ip="${server_ip}" '
   323                 BEGIN {
   324                     split( server_ip, tmp, "\\." );
   325                     server_ip_num = tmp[1] * 2^24 + tmp[2] * 2^16 + tmp[3] * 2^8 + tmp[4] * 2^0;
   326                     pairs = 0;
   327                 }
   328 
   329                 $0 ~ /^[[:space:]]*inet addr:/ {
   330                     split( $2, tmp, ":|\\." );
   331                     if( ( tmp[2] == 127 ) && ( tmp[3] == 0 ) && ( tmp[4] == 0 ) && ( tmp[5] == 1 ) ) {
   332                         /* Skip 127.0.0.1, it'\''s taken care of by tsocks itself */
   333                         next;
   334                     }
   335                     ip_num = tmp[2] * 2^24 + tmp[3] * 2^16 + tmp[4] * 2 ^8 + tmp[5] * 2^0;
   336                     i = 32;
   337                     do {
   338                         i--;
   339                         mask = 2^32 - 2^i;
   340                     } while( (i!=0) && ( and( server_ip_num, mask ) == and( ip_num, mask ) ) );
   341                     mask = and( 0xFFFFFFFF, lshift( mask, 1 ) );
   342                     if( (i!=0) && (mask!=0) ) {
   343                         masked_ip = and( ip_num, mask );
   344                         for( i=0; i<pairs; i++ ) {
   345                             if( ( masked_ip == ips[i] ) && ( mask == masks[i] ) ) {
   346                                 next;
   347                             }
   348                         }
   349                         ips[pairs] = masked_ip;
   350                         masks[pairs] = mask;
   351                         pairs++;
   352                         printf( "local = %d.%d.%d.%d/%d.%d.%d.%d\n",
   353                                 and( 0xFF, masked_ip / 2^24 ),
   354                                 and( 0xFF, masked_ip / 2^16 ),
   355                                 and( 0xFF, masked_ip / 2^8 ),
   356                                 and( 0xFF, masked_ip / 2^0 ),
   357                                 and( 0xFF, mask / 2^24 ),
   358                                 and( 0xFF, mask / 2^16 ),
   359                                 and( 0xFF, mask / 2^8 ),
   360                                 and( 0xFF, mask / 2^0 ) );
   361                     }
   362                 }
   363             ' >"${CT_BUILD_DIR}/tsocks.conf"
   364             ( echo "server = ${server_ip}";
   365               echo "server_port = ${CT_PROXY_PORT}";
   366               [ -n "${CT_PROXY_USER}"   ] && echo "default_user=${CT_PROXY_USER}";
   367               [ -n "${CT_PROXY_PASS}" ] && echo "default_pass=${CT_PROXY_PASS}";
   368             ) >>"${CT_BUILD_DIR}/tsocks.conf"
   369             case "${CT_PROXY_TYPE/socks}" in
   370                 4|5) proxy_type="${CT_PROXY_TYPE/socks}";;
   371                 auto)
   372                     reply=$(inspectsocks "${server_ip}" "${CT_PROXY_PORT}" 2>&1 || true)
   373                     case "${reply}" in
   374                         *"server is a version 4 socks server") proxy_type=4;;
   375                         *"server is a version 5 socks server") proxy_type=5;;
   376                         *) CT_Abort "Unable to determine SOCKS proxy type for '${CT_PROXY_HOST}:${CT_PROXY_PORT}'"
   377                     esac
   378                     ;;
   379             esac
   380             echo "server_type = ${proxy_type}" >> "${CT_BUILD_DIR}/tsocks.conf"
   381             CT_HasOrAbort tsocks
   382             # If tsocks was found, then validateconf is present (distributed with tsocks).
   383             CT_DoExecLog DEBUG validateconf -f "${CT_BUILD_DIR}/tsocks.conf"
   384             export TSOCKS_CONF_FILE="${CT_BUILD_DIR}/tsocks.conf"
   385             . tsocks -on
   386             ;;
   387     esac
   388 }
   389 
   390 # Download an URL using wget
   391 # Usage: CT_DoGetFileWget <URL>
   392 CT_DoGetFileWget() {
   393     # Need to return true because it is legitimate to not find the tarball at
   394     # some of the provided URLs (think about snapshots, different layouts for
   395     # different gcc versions, etc...)
   396     # Some (very old!) FTP server might not support the passive mode, thus
   397     # retry without
   398     # With automated download as we are doing, it can be very dangerous to use
   399     # -c to continue the downloads. It's far better to simply overwrite the
   400     # destination file
   401     # Some company networks have firewalls to connect to the internet, but it's
   402     # not easy to detect them, and wget does not timeout by default  while
   403     # connecting, so force a global ${CT_CONNECT_TIMEOUT}-second timeout.
   404     wget -T ${CT_CONNECT_TIMEOUT} -nc --progress=dot:binary --tries=3 --passive-ftp "$1"    \
   405     || wget -T ${CT_CONNECT_TIMEOUT} -nc --progress=dot:binary --tries=3 "$1"               \
   406     || true
   407 }
   408 
   409 # Download an URL using curl
   410 # Usage: CT_DoGetFileCurl <URL>
   411 CT_DoGetFileCurl() {
   412     # Note: comments about wget method (above) are also valid here
   413     # Plus: no good progress indicator is available with curl,
   414     #       so output is consigned to oblivion
   415     curl --ftp-pasv -O --retry 3 "$1" --connect-timeout ${CT_CONNECT_TIMEOUT} >/dev/null    \
   416     || curl -O --retry 3 "$1" --connect-timeout ${CT_CONNECT_TIMEOUT} >/dev/null            \
   417     || true
   418 }
   419 
   420 _wget=$(CT_Which wget)
   421 _curl=$(CT_Which curl)
   422 # Wrapper function to call one of curl or wget
   423 # Usage: CT_DoGetFile <URL>
   424 CT_DoGetFile() {
   425     case "${_wget},${_curl}" in
   426         ,)  CT_DoError "Could find neither wget nor curl";;
   427         ,*) CT_DoExecLog ALL CT_DoGetFileCurl "$1" 2>&1;;
   428         *)  CT_DoExecLog ALL CT_DoGetFileWget "$1" 2>&1;;
   429     esac
   430 }
   431 
   432 # Download the file from one of the URLs passed as argument
   433 # Usage: CT_GetFile <filename> [extension] <url> [url ...]
   434 CT_GetFile() {
   435     local ext
   436     local url
   437     local file="$1"
   438     local first_ext=""
   439     shift
   440     # If next argument starts with a dot, then this is not an URL,
   441     # and we can consider that it is a preferred extension.
   442     case "$1" in
   443         .*) first_ext="$1"
   444             shift
   445             ;;
   446     esac
   447 
   448     # Do we already have it?
   449     ext=$(CT_GetFileExtension "${file}" ${first_ext})
   450     if [ -n "${ext}" ]; then
   451         CT_DoLog DEBUG "Already have '${file}'"
   452         return 0
   453     fi
   454 
   455     # Try to retrieve the file
   456     CT_DoLog EXTRA "Retrieving '${file}'"
   457     CT_Pushd "${CT_TARBALLS_DIR}"
   458 
   459     if [ -n "${CT_LOCAL_TARBALLS_DIR}" ]; then
   460         CT_DoLog DEBUG "Trying to retrieve an already downloaded copy of '${file}'"
   461         # We'd rather have a bzip2'ed tarball, then gzipped tarball, plain tarball,
   462         # or, as a failover, a file without extension.
   463         # Try local copy first, if it exists
   464         for ext in ${first_ext} .tar.bz2 .tar.gz .tgz .tar ''; do
   465             CT_DoLog DEBUG "Trying '${CT_LOCAL_TARBALLS_DIR}/${file}${ext}'"
   466             if [ -r "${CT_LOCAL_TARBALLS_DIR}/${file}${ext}" -a \
   467                  "${CT_FORCE_DOWNLOAD}" != "y" ]; then
   468                 CT_DoLog DEBUG "Got '${file}' from local storage"
   469                 CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${file}${ext}" "${file}${ext}"
   470                 return 0
   471             fi
   472         done
   473     fi
   474 
   475     # Not found locally, try from the network
   476 
   477     # Start with LAN mirror
   478     if [ "${CT_USE_LAN_MIRROR}" = "y" ]; then
   479         CT_DoSetProxy ${CT_LAN_MIRROR_USE_PROXY:+${CT_PROXY_TYPE}}
   480         CT_DoLog DEBUG "Trying to retrieve a copy of '${file}' from LAN mirror '${CT_LAN_MIRROR_HOSTNAME}'"
   481         CT_TestOrAbort "Please set the LAN mirror hostname" -n "${CT_LAN_MIRROR_HOSTNAME}"
   482         CT_TestOrAbort "Please tell me where to find tarballs on the LAN mirror '${CT_LAN_MIRROR_HOSTNAME}'" -n "${CT_LAN_MIRROR_BASE}"
   483         for ext in ${first_ext} .tar.bz2 .tar.gz .tgz .tar ''; do
   484             # Please note: we just have the file's basename in a single piece.
   485             # So we have to just try and split it back into name and version... :-(
   486             for url in "${CT_LAN_MIRROR_SCHEME}://${CT_LAN_MIRROR_HOSTNAME}/${CT_LAN_MIRROR_BASE}/${file%-*}"  \
   487                        "${CT_LAN_MIRROR_SCHEME}://${CT_LAN_MIRROR_HOSTNAME}/${CT_LAN_MIRROR_BASE}";            \
   488             do
   489                 CT_DoLog DEBUG "Trying '${url}/${file}${ext}'"
   490                 CT_DoGetFile "${url}/${file}${ext}"
   491                 if [ -f "${file}${ext}" ]; then
   492                     CT_DoLog DEBUG "Got '${file}' from the LAN mirror"
   493                     if [ "${CT_SAVE_TARBALLS}" = "y" ]; then
   494                         # The file may already exist if downloads are forced: remove it first
   495                         CT_DoLog EXTRA "Saving '${file}' to local storage"
   496                         CT_DoExecLog ALL rm -f "${CT_LOCAL_TARBALLS_DIR}/${file}${ext}"
   497                         CT_DoExecLog ALL mv -f "${file}${ext}" "${CT_LOCAL_TARBALLS_DIR}"
   498                         CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${file}${ext}" "${file}${ext}"
   499                     fi
   500                     return 0
   501                 fi
   502             done
   503         done
   504     fi
   505 
   506     # OK, available neither localy, nor from the LAN mirror (if any).
   507     CT_DoSetProxy ${CT_PROXY_TYPE}
   508     for ext in ${first_ext} .tar.bz2 .tar.gz .tgz .tar ''; do
   509         # Try all urls in turn
   510         for url in "$@"; do
   511             CT_DoLog DEBUG "Trying '${url}/${file}${ext}'"
   512             CT_DoGetFile "${url}/${file}${ext}"
   513             if [ -f "${file}${ext}" ]; then
   514                 CT_DoLog DEBUG "Got '${file}' from the Internet"
   515                 if [ "${CT_SAVE_TARBALLS}" = "y" ]; then
   516                     # The file may already exist if downloads are forced: remove it first
   517                     CT_DoLog EXTRA "Saving '${file}' to local storage"
   518                     CT_DoExecLog ALL rm -f "${CT_LOCAL_TARBALLS_DIR}/${file}${ext}"
   519                     CT_DoExecLog ALL mv -f "${file}${ext}" "${CT_LOCAL_TARBALLS_DIR}"
   520                     CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${file}${ext}" "${file}${ext}"
   521                 fi
   522                 return 0
   523             fi
   524         done
   525     done
   526     CT_Popd
   527 
   528     CT_Abort "Could not retrieve '${file}'."
   529 }
   530 
   531 # Extract a tarball and patch the resulting sources if necessary.
   532 # Some tarballs need to be extracted in specific places. Eg.: glibc addons
   533 # must be extracted in the glibc directory; uCLibc locales must be extracted
   534 # in the extra/locale sub-directory of uClibc.
   535 CT_ExtractAndPatch() {
   536     local file="$1"
   537     local base_file=$(echo "${file}" |cut -d - -f 1)
   538     local ver_file=$(echo "${file}" |cut -d - -f 2-)
   539     local official_patch_dir
   540     local custom_patch_dir
   541     local libc_addon
   542     local ext=$(CT_GetFileExtension "${file}")
   543     CT_TestAndAbort "'${file}' not found in '${CT_TARBALLS_DIR}'" -z "${ext}"
   544     local full_file="${CT_TARBALLS_DIR}/${file}${ext}"
   545 
   546     CT_Pushd "${CT_SRC_DIR}"
   547 
   548     # Add-ons need a little love, really.
   549     case "${file}" in
   550         glibc-[a-z]*-*|eglibc-[a-z]*-*)
   551             CT_TestAndAbort "Trying to extract the C-library addon/locales '${file}' when C-library not yet extracted" ! -d "${CT_LIBC_FILE}"
   552             cd "${CT_LIBC_FILE}"
   553             libc_addon=y
   554             [ -f ".${file}.extracted" ] && return 0
   555             touch ".${file}.extracted"
   556             ;;
   557         uClibc-locale-*)
   558             CT_TestAndAbort "Trying to extract the C-library addon/locales '${file}' when C-library not yet extracted" ! -d "${CT_LIBC_FILE}"
   559             cd "${CT_LIBC_FILE}/extra/locale"
   560             libc_addon=y
   561             [ -f ".${file}.extracted" ] && return 0
   562             touch ".${file}.extracted"
   563             ;;
   564     esac
   565 
   566     # If the directory exists, then consider extraction and patching done
   567     if [ -d "${file}" ]; then
   568         CT_DoLog DEBUG "Already extracted '${file}'"
   569         return 0
   570     fi
   571 
   572     CT_DoLog EXTRA "Extracting and patching '${file}'"
   573     case "${ext}" in
   574         .tar.bz2)     CT_DoExecLog ALL tar xvjf "${full_file}";;
   575         .tar.gz|.tgz) CT_DoExecLog ALL tar xvzf "${full_file}";;
   576         .tar)         CT_DoExecLog ALL tar xvf  "${full_file}";;
   577         *)            CT_Abort "Don't know how to handle '${file}': unknown extension" ;;
   578     esac
   579 
   580     # Snapshots might not have the version number in the extracted directory
   581     # name. This is also the case for some (odd) packages, such as D.U.M.A.
   582     # Overcome this issue by symlink'ing the directory.
   583     if [ ! -d "${file}" -a "${libc_addon}" != "y" ]; then
   584         case "${ext}" in
   585             .tar.bz2)     base=$(tar tjf "${full_file}" |head -n 1 |cut -d / -f 1 || true);;
   586             .tar.gz|.tgz) base=$(tar tzf "${full_file}" |head -n 1 |cut -d / -f 1 || true);;
   587             .tar)         base=$(tar tf  "${full_file}" |head -n 1 |cut -d / -f 1 || true);;
   588         esac
   589         CT_TestOrAbort "There was a problem when extracting '${file}'" -d "${base}" -o "${base}" != "${file}"
   590         ln -s "${base}" "${file}"
   591     fi
   592 
   593     # Kludge: outside this function, we wouldn't know if we had just extracted
   594     # a libc addon, or a plain package. Apply patches now.
   595     if [ "${libc_addon}" = "y" ]; then
   596         # Some addon tarballs directly contain the correct addon directory,
   597         # while others have the addon directory named after the tarball.
   598         # Fix that by always using the short name (eg: linuxthreads, ports, etc...)
   599         addon_short_name=$(echo "${file}" |sed -r -e 's/^[^-]+-//; s/-[^-]+$//;')
   600         [ -d "${addon_short_name}" ] || ln -s "${file}" "${addon_short_name}"
   601         # If libc addon, we're already in the correct place
   602     else
   603         cd "${file}"
   604     fi
   605 
   606     official_patch_dir=
   607     custom_patch_dir=
   608     [ "${CUSTOM_PATCH_ONLY}" = "y" ] || official_patch_dir="${CT_LIB_DIR}/patches/${base_file}/${ver_file}"
   609     [ "${CT_CUSTOM_PATCH}" = "y" ] && custom_patch_dir="${CT_CUSTOM_PATCH_DIR}/${base_file}/${ver_file}"
   610     for patch_dir in "${official_patch_dir}" "${custom_patch_dir}"; do
   611         if [ -n "${patch_dir}" -a -d "${patch_dir}" ]; then
   612             for p in "${patch_dir}"/*.patch; do
   613                 if [ -f "${p}" ]; then
   614                     CT_DoLog DEBUG "Applying patch '${p}'"
   615                     CT_DoExecLog ALL patch -g0 -F1 -p1 -f <"${p}"
   616                     CT_TestAndAbort "Failed while applying patch file '${p}'" ${PIPESTATUS[0]} -ne 0
   617                 fi
   618             done
   619         fi
   620     done
   621 
   622     if [ "${CT_OVERIDE_CONFIG_GUESS_SUB}" = "y" ]; then
   623         CT_DoLog ALL "Overiding config.guess and config.sub"
   624         for cfg in config_guess config_sub; do
   625             eval ${cfg}="${CT_LIB_DIR}/tools/${cfg/_/.}"
   626             [ -e "${CT_TOP_DIR}/tools/${cfg/_/.}" ] && eval ${cfg}="${CT_TOP_DIR}/tools/${cfg/_/.}"
   627             # Can't use CT_DoExecLog because of the '{} \;' to be passed un-mangled to find
   628             find . -type f -name "${cfg/_/.}" -exec cp -v "${!cfg}" {} \; |CT_DoLog ALL
   629         done
   630     fi
   631 
   632     CT_Popd
   633 }
   634 
   635 # Two wrappers to call config.(guess|sub) either from CT_TOP_DIR or CT_LIB_DIR.
   636 # Those from CT_TOP_DIR, if they exist, will be be more recent than those from CT_LIB_DIR.
   637 CT_DoConfigGuess() {
   638     if [ -x "${CT_TOP_DIR}/tools/config.guess" ]; then
   639         "${CT_TOP_DIR}/tools/config.guess"
   640     else
   641         "${CT_LIB_DIR}/tools/config.guess"
   642     fi
   643 }
   644 
   645 CT_DoConfigSub() {
   646     if [ -x "${CT_TOP_DIR}/tools/config.sub" ]; then
   647         "${CT_TOP_DIR}/tools/config.sub" "$@"
   648     else
   649         "${CT_LIB_DIR}/tools/config.sub" "$@"
   650     fi
   651 }
   652 
   653 # Compute the target tuple from what is provided by the user
   654 # Usage: CT_DoBuildTargetTuple
   655 # In fact this function takes the environment variables to build the target
   656 # tuple. It is needed both by the normal build sequence, as well as the
   657 # sample saving sequence.
   658 CT_DoBuildTargetTuple() {
   659     # Set the endianness suffix, and the default endianness gcc option
   660     case "${CT_ARCH_BE},${CT_ARCH_LE}" in
   661         y,) target_endian_eb=eb
   662             target_endian_el=
   663             CT_ARCH_ENDIAN_CFLAG="-mbig-endian"
   664             CT_ARCH_ENDIAN_LDFLAG="-EB"
   665             ;;
   666         ,y) target_endian_eb=
   667             target_endian_el=el
   668             CT_ARCH_ENDIAN_CFLAG="-mlittle-endian"
   669             CT_ARCH_ENDIAN_LDFLAG="-EL"
   670             ;;
   671     esac
   672 
   673     # Set defaults for the system part of the tuple. Can be overriden
   674     # by architecture-specific values.
   675     case "${CT_LIBC}" in
   676         none)   CT_TARGET_SYS=elf;;
   677         *glibc) CT_TARGET_SYS=gnu;;
   678         uClibc) CT_TARGET_SYS=uclibc;;
   679     esac
   680 
   681     # Transform the ARCH into a kernel-understandable ARCH
   682     CT_KERNEL_ARCH="${CT_ARCH}"
   683 
   684     # Set the default values for ARCH, ABI, CPU, TUNE, FPU and FLOAT
   685     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
   686     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
   687     [ "${CT_ARCH_ARCH}"     ] && { CT_ARCH_ARCH_CFLAG="-march=${CT_ARCH_ARCH}";  CT_ARCH_WITH_ARCH="--with-arch=${CT_ARCH_ARCH}"; }
   688     [ "${CT_ARCH_ABI}"      ] && { CT_ARCH_ABI_CFLAG="-mabi=${CT_ARCH_ABI}";     CT_ARCH_WITH_ABI="--with-abi=${CT_ARCH_ABI}";    }
   689     [ "${CT_ARCH_CPU}"      ] && { CT_ARCH_CPU_CFLAG="-mcpu=${CT_ARCH_CPU}";     CT_ARCH_WITH_CPU="--with-cpu=${CT_ARCH_CPU}";    }
   690     [ "${CT_ARCH_TUNE}"     ] && { CT_ARCH_TUNE_CFLAG="-mtune=${CT_ARCH_TUNE}";  CT_ARCH_WITH_TUNE="--with-tune=${CT_ARCH_TUNE}"; }
   691     [ "${CT_ARCH_FPU}"      ] && { CT_ARCH_FPU_CFLAG="-mfpu=${CT_ARCH_FPU}";     CT_ARCH_WITH_FPU="--with-fpu=${CT_ARCH_FPU}";    }
   692     [ "${CT_ARCH_FLOAT_SW}" ] && { CT_ARCH_FLOAT_CFLAG="-msoft-float";           CT_ARCH_WITH_FLOAT="--with-float=soft";          }
   693 
   694     # Call the architecture specific settings
   695     CT_DoArchValues
   696 
   697     # Finish the target tuple construction
   698     case "${CT_KERNEL}" in
   699         none)   CT_TARGET_KERNEL=;;
   700         linux)  CT_TARGET_KERNEL=linux-;;
   701     esac
   702     CT_TARGET=$(CT_DoConfigSub "${CT_TARGET_ARCH}-${CT_TARGET_VENDOR:-unknown}-${CT_TARGET_KERNEL}${CT_TARGET_SYS}")
   703 
   704     # Prepare the target CFLAGS
   705     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ENDIAN_CFLAG}"
   706     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ARCH_CFLAG}"
   707     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ABI_CFLAG}"
   708     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_CPU_CFLAG}"
   709     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_TUNE_CFLAG}"
   710     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FPU_CFLAG}"
   711     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FLOAT_CFLAG}"
   712 
   713     # Now on for the target LDFLAGS
   714     CT_ARCH_TARGET_LDFLAGS="${CT_ARCH_TARGET_LDFLAGS} ${CT_ARCH_ENDIAN_LDFLAG}"
   715 }
   716 
   717 # This function does pause the build until the user strikes "Return"
   718 # Usage: CT_DoPause [optional_message]
   719 CT_DoPause() {
   720     local foo
   721     local message="${1:-Pausing for your pleasure}"
   722     CT_DoLog INFO "${message}"
   723     read -p "Press 'Enter' to continue, or Ctrl-C to stop..." foo >&6
   724     return 0
   725 }
   726 
   727 # This function saves the state of the toolchain to be able to restart
   728 # at any one point
   729 # Usage: CT_DoSaveState <next_step_name>
   730 CT_DoSaveState() {
   731 	[ "${CT_DEBUG_CT_SAVE_STEPS}" = "y" ] || return 0
   732     local state_name="$1"
   733     local state_dir="${CT_STATE_DIR}/${state_name}"
   734 
   735     CT_DoLog DEBUG "Saving state to restart at step '${state_name}'..."
   736     rm -rf "${state_dir}"
   737     mkdir -p "${state_dir}"
   738 
   739     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   740         y)  tar_opt=z; tar_ext=.gz;;
   741         *)  tar_opt=;  tar_ext=;;
   742     esac
   743 
   744     CT_DoLog DEBUG "  Saving environment and aliases"
   745     # We must omit shell functions, and some specific bash variables
   746     # that break when restoring the environment, later. We could do
   747     # all the processing in the awk script, but a sed is easier...
   748     set |awk '
   749              BEGIN { _p = 1; }
   750              $0~/^[^ ]+ \(\)/ { _p = 0; }
   751              _p == 1
   752              $0 == "}" { _p = 1; }
   753              ' |sed -r -e '/^BASH_(ARGC|ARGV|LINENO|SOURCE|VERSINFO)=/d;
   754                            /^(UID|EUID)=/d;
   755                            /^(FUNCNAME|GROUPS|PPID|SHELLOPTS)=/d;' >"${state_dir}/env.sh"
   756 
   757     CT_DoLog DEBUG "  Saving CT_CC_CORE_STATIC_PREFIX_DIR='${CT_CC_CORE_STATIC_PREFIX_DIR}'"
   758     CT_Pushd "${CT_CC_CORE_STATIC_PREFIX_DIR}"
   759     CT_DoExecLog DEBUG tar cv${tar_opt}f "${state_dir}/cc_core_static_prefix_dir.tar${tar_ext}" .
   760     CT_Popd
   761 
   762     CT_DoLog DEBUG "  Saving CT_CC_CORE_SHARED_PREFIX_DIR='${CT_CC_CORE_SHARED_PREFIX_DIR}'"
   763     CT_Pushd "${CT_CC_CORE_SHARED_PREFIX_DIR}"
   764     CT_DoExecLog DEBUG tar cv${tar_opt}f "${state_dir}/cc_core_shared_prefix_dir.tar${tar_ext}" .
   765     CT_Popd
   766 
   767     CT_DoLog DEBUG "  Saving CT_PREFIX_DIR='${CT_PREFIX_DIR}'"
   768     CT_Pushd "${CT_PREFIX_DIR}"
   769     CT_DoExecLog DEBUG tar cv${tar_opt}f "${state_dir}/prefix_dir.tar${tar_ext}" --exclude '*.log' .
   770     CT_Popd
   771 
   772     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
   773         CT_DoLog DEBUG "  Saving log file"
   774         exec >/dev/null
   775         case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   776             y)  gzip -3 -c "${CT_LOG_FILE}"  >"${state_dir}/log.gz";;
   777             *)  cat "${CT_LOG_FILE}" >"${state_dir}/log";;
   778         esac
   779         exec >>"${CT_LOG_FILE}"
   780     fi
   781 }
   782 
   783 # This function restores a previously saved state
   784 # Usage: CT_DoLoadState <state_name>
   785 CT_DoLoadState(){
   786     local state_name="$1"
   787     local state_dir="${CT_STATE_DIR}/${state_name}"
   788     local old_RESTART="${CT_RESTART}"
   789     local old_STOP="${CT_STOP}"
   790 
   791     CT_TestOrAbort "The previous build did not reach the point where it could be restarted at '${CT_RESTART}'" -d "${state_dir}"
   792 
   793     # We need to do something special with the log file!
   794     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
   795         exec >"${state_dir}/tail.log"
   796     fi
   797     CT_DoLog INFO "Restoring state at step '${state_name}', as requested."
   798 
   799     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   800         y)  tar_opt=z; tar_ext=.gz;;
   801         *)  tar_opt=;  tar_ext=;;
   802     esac
   803 
   804     CT_DoLog DEBUG "  Removing previous build directories"
   805     chmod -R u+rwX "${CT_PREFIX_DIR}" "${CT_CC_CORE_SHARED_PREFIX_DIR}" "${CT_CC_CORE_STATIC_PREFIX_DIR}"
   806     rm -rf         "${CT_PREFIX_DIR}" "${CT_CC_CORE_SHARED_PREFIX_DIR}" "${CT_CC_CORE_STATIC_PREFIX_DIR}"
   807     mkdir -p       "${CT_PREFIX_DIR}" "${CT_CC_CORE_SHARED_PREFIX_DIR}" "${CT_CC_CORE_STATIC_PREFIX_DIR}"
   808 
   809     CT_DoLog DEBUG "  Restoring CT_PREFIX_DIR='${CT_PREFIX_DIR}'"
   810     CT_Pushd "${CT_PREFIX_DIR}"
   811     CT_DoExecLog DEBUG tar xv${tar_opt}f "${state_dir}/prefix_dir.tar${tar_ext}"
   812     CT_Popd
   813 
   814     CT_DoLog DEBUG "  Restoring CT_CC_CORE_SHARED_PREFIX_DIR='${CT_CC_CORE_SHARED_PREFIX_DIR}'"
   815     CT_Pushd "${CT_CC_CORE_SHARED_PREFIX_DIR}"
   816     CT_DoExecLog DEBUG tar xv${tar_opt}f "${state_dir}/cc_core_shared_prefix_dir.tar${tar_ext}"
   817     CT_Popd
   818 
   819     CT_DoLog DEBUG "  Restoring CT_CC_CORE_STATIC_PREFIX_DIR='${CT_CC_CORE_STATIC_PREFIX_DIR}'"
   820     CT_Pushd "${CT_CC_CORE_STATIC_PREFIX_DIR}"
   821     CT_DoExecLog DEBUG tar xv${tar_opt}f "${state_dir}/cc_core_static_prefix_dir.tar${tar_ext}"
   822     CT_Popd
   823 
   824     # Restore the environment, discarding any error message
   825     # (for example, read-only bash internals)
   826     CT_DoLog DEBUG "  Restoring environment"
   827     . "${state_dir}/env.sh" >/dev/null 2>&1 || true
   828 
   829     # Restore the new RESTART and STOP steps
   830     CT_RESTART="${old_RESTART}"
   831     CT_STOP="${old_STOP}"
   832     unset old_stop old_restart
   833 
   834     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
   835         CT_DoLog DEBUG "  Restoring log file"
   836         exec >/dev/null
   837         case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   838             y)  zcat "${state_dir}/log.gz" >"${CT_LOG_FILE}";;
   839             *)  cat "${state_dir}/log" >"${CT_LOG_FILE}";;
   840         esac
   841         cat "${state_dir}/tail.log" >>"${CT_LOG_FILE}"
   842         exec >>"${CT_LOG_FILE}"
   843         rm -f "${state_dir}/tail.log"
   844     fi
   845 }