scripts/functions
author Bart vdr. Meulen <bartvdrmeulen@gmail.com>
Sun Aug 02 16:45:43 2009 +0200 (2009-08-02)
changeset 1434 7e7290acccc1
parent 1391 3c28b9f917d2
child 1506 d33da84b1317
permissions -rw-r--r--
fix wrapper script for symlinks

The wrapper script placed around the target binaries when
using the companion libraries does not work for symbolic links
The wrapper scripts needs to follow the links before calling the
actual binary

Signed-off-by: Bart vdr. Meulen <bartvdrmeulen@gmail.com>

---
     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 # Make it easy to use \n and !
    56 CR=$(printf "\n")
    57 BANG='!'
    58 
    59 # A function to log what is happening
    60 # Different log level are available:
    61 #   - ERROR:   A serious, fatal error occurred
    62 #   - WARN:    A non fatal, non serious error occurred, take your responsbility with the generated build
    63 #   - INFO:    Informational messages
    64 #   - EXTRA:   Extra informational messages
    65 #   - DEBUG:   Debug messages
    66 #   - ALL:     Component's build messages
    67 # Usage: CT_DoLog <level> [message]
    68 # If message is empty, then stdin will be logged.
    69 CT_DoLog() {
    70     local max_level LEVEL level cur_l cur_L
    71     local l
    72     eval max_level="\${CT_LOG_LEVEL_${CT_LOG_LEVEL_MAX}}"
    73     # Set the maximum log level to DEBUG if we have none
    74     [ -z "${max_level}" ] && max_level=${CT_LOG_LEVEL_DEBUG}
    75 
    76     LEVEL="$1"; shift
    77     eval level="\${CT_LOG_LEVEL_${LEVEL}}"
    78 
    79     if [ $# -eq 0 ]; then
    80         cat -
    81     else
    82         printf "${@}\n"
    83     fi |( IFS="${CR}" # We want the full lines, even leading spaces
    84           _prog_bar_cpt=0
    85           _prog_bar[0]='/'
    86           _prog_bar[1]='-'
    87           _prog_bar[2]='\'
    88           _prog_bar[3]='|'
    89           indent=$((2*CT_STEP_COUNT))
    90           while read line; do
    91               case "${CT_LOG_SEE_TOOLS_WARN},${line}" in
    92                 y,*"warning:"*)         cur_L=WARN; cur_l=${CT_LOG_LEVEL_WARN};;
    93                 y,*"WARNING:"*)         cur_L=WARN; cur_l=${CT_LOG_LEVEL_WARN};;
    94                 *"error:"*)             cur_L=ERROR; cur_l=${CT_LOG_LEVEL_ERROR};;
    95                 *"make["*"]: *** ["*)   cur_L=ERROR; cur_l=${CT_LOG_LEVEL_ERROR};;
    96                 *)                      cur_L="${LEVEL}"; cur_l="${level}";;
    97               esac
    98               # There will always be a log file (stdout, fd #1), be it /dev/null
    99               printf "[%-5s]%*s%s%s\n" "${cur_L}" "${indent}" " " "${line}"
   100               if [ ${cur_l} -le ${max_level} ]; then
   101                   # Only print to console (fd #6) if log level is high enough.
   102                   printf "\r[%-5s]%*s%s%s\n" "${cur_L}" "${indent}" " " "${line}" >&6
   103               fi
   104               if [ "${CT_LOG_PROGRESS_BAR}" = "y" ]; then
   105                   printf "\r[%02d:%02d] %s " $((SECONDS/60)) $((SECONDS%60)) "${_prog_bar[$((_prog_bar_cpt/10))]}" >&6
   106                   _prog_bar_cpt=$(((_prog_bar_cpt+1)%40))
   107               fi
   108           done
   109         )
   110 
   111     return 0
   112 }
   113 
   114 # Execute an action, and log its messages
   115 # Usage: [VAR=val...] CT_DoExecLog <level> <command [parameters...]>
   116 CT_DoExecLog() {
   117     local level="$1"
   118     shift
   119     CT_DoLog DEBUG "==> Executing: '${@}'"
   120     "${@}" 2>&1 |CT_DoLog "${level}"
   121 }
   122 
   123 # Tail message to be logged whatever happens
   124 # Usage: CT_DoEnd <level>
   125 CT_DoEnd()
   126 {
   127     local level="$1"
   128     CT_STOP_DATE=$(CT_DoDate +%s%N)
   129     CT_STOP_DATE_HUMAN=$(CT_DoDate +%Y%m%d.%H%M%S)
   130     if [ "${level}" != "ERROR" ]; then
   131         CT_DoLog "${level:-INFO}" "Build completed at ${CT_STOP_DATE_HUMAN}"
   132     fi
   133     elapsed=$((CT_STOP_DATE-CT_STAR_DATE))
   134     elapsed_min=$((elapsed/(60*1000*1000*1000)))
   135     elapsed_sec=$(printf "%02d" $(((elapsed%(60*1000*1000*1000))/(1000*1000*1000))))
   136     elapsed_csec=$(printf "%02d" $(((elapsed%(1000*1000*1000))/(10*1000*1000))))
   137     CT_DoLog ${level:-INFO} "(elapsed: ${elapsed_min}:${elapsed_sec}.${elapsed_csec})"
   138 }
   139 
   140 # Abort the execution with an error message
   141 # Usage: CT_Abort <message>
   142 CT_Abort() {
   143     CT_DoLog ERROR "$1"
   144     exit 1
   145 }
   146 
   147 # Test a condition, and print a message if satisfied
   148 # Usage: CT_Test <message> <tests>
   149 CT_Test() {
   150     local ret
   151     local m="$1"
   152     shift
   153     test "$@" && CT_DoLog WARN "$m"
   154     return 0
   155 }
   156 
   157 # Test a condition, and abort with an error message if satisfied
   158 # Usage: CT_TestAndAbort <message> <tests>
   159 CT_TestAndAbort() {
   160     local m="$1"
   161     shift
   162     test "$@" && CT_Abort "$m"
   163     return 0
   164 }
   165 
   166 # Test a condition, and abort with an error message if not satisfied
   167 # Usage: CT_TestAndAbort <message> <tests>
   168 CT_TestOrAbort() {
   169     local m="$1"
   170     shift
   171     test "$@" || CT_Abort "$m"
   172     return 0
   173 }
   174 
   175 # Test the presence of a tool, or abort if not found
   176 # Usage: CT_HasOrAbort <tool>
   177 CT_HasOrAbort() {
   178     CT_TestAndAbort "'${1}' not found and needed for successful toolchain build." -z "$(CT_Which "${1}")"
   179     return 0
   180 }
   181 
   182 # Search a program: wrap "which" for those system where
   183 # "which" verbosely says there is no match (Mandriva is
   184 # such a sucker...)
   185 # Usage: CT_Which <filename>
   186 CT_Which() {
   187   which "$1" 2>/dev/null || true
   188 }
   189 
   190 # Get current date with nanosecond precision
   191 # On those system not supporting nanosecond precision, faked with rounding down
   192 # to the highest entire second
   193 # Usage: CT_DoDate <fmt>
   194 CT_DoDate() {
   195     date "$1" |sed -r -e 's/%N$/000000000/;'
   196 }
   197 
   198 CT_STEP_COUNT=1
   199 CT_STEP_MESSAGE[${CT_STEP_COUNT}]="<none>"
   200 # Memorise a step being done so that any error is caught
   201 # Usage: CT_DoStep <loglevel> <message>
   202 CT_DoStep() {
   203     local start=$(CT_DoDate +%s%N)
   204     CT_DoLog "$1" "================================================================="
   205     CT_DoLog "$1" "$2"
   206     CT_STEP_COUNT=$((CT_STEP_COUNT+1))
   207     CT_STEP_LEVEL[${CT_STEP_COUNT}]="$1"; shift
   208     CT_STEP_START[${CT_STEP_COUNT}]="${start}"
   209     CT_STEP_MESSAGE[${CT_STEP_COUNT}]="$1"
   210     return 0
   211 }
   212 
   213 # End the step just being done
   214 # Usage: CT_EndStep
   215 CT_EndStep() {
   216     local stop=$(CT_DoDate +%s%N)
   217     local duration=$(printf "%032d" $((stop-${CT_STEP_START[${CT_STEP_COUNT}]})) |sed -r -e 's/([[:digit:]]{2})[[:digit:]]{7}$/\.\1/; s/^0+//; s/^\./0\./;')
   218     local elapsed=$(printf "%02d:%02d" $((SECONDS/60)) $((SECONDS%60)))
   219     local level="${CT_STEP_LEVEL[${CT_STEP_COUNT}]}"
   220     local message="${CT_STEP_MESSAGE[${CT_STEP_COUNT}]}"
   221     CT_STEP_COUNT=$((CT_STEP_COUNT-1))
   222     CT_DoLog "${level}" "${message}: done in ${duration}s (at ${elapsed})"
   223     return 0
   224 }
   225 
   226 # Pushes into a directory, and pops back
   227 CT_Pushd() {
   228     pushd "$1" >/dev/null 2>&1
   229 }
   230 CT_Popd() {
   231     popd >/dev/null 2>&1
   232 }
   233 
   234 # Creates a temporary directory
   235 # $1: variable to assign to
   236 # Usage: CT_MktempDir foo
   237 CT_MktempDir() {
   238     # Some mktemp do not allow more than 6 Xs
   239     eval "$1"=$(mktemp -q -d "${CT_BUILD_DIR}/tmp.XXXXXX")
   240     CT_TestOrAbort "Could not make temporary directory" -n "${!1}" -a -d "${!1}"
   241     CT_DoLog DEBUG "Made temporary directory '${!1}'"
   242     return 0
   243 }
   244 
   245 # Removes one or more directories, even if it is read-only, or its parent is
   246 # Usage: CT_DoForceRmdir dir [...]
   247 CT_DoForceRmdir() {
   248     local dir
   249     local mode
   250     for dir in "${@}"; do
   251         [ -d "${dir}" ] || continue
   252         mode="$(stat -c '%a' "$(dirname "${dir}")")"
   253         CT_DoExecLog ALL chmod u+w "$(dirname "${dir}")"
   254         CT_DoExecLog ALL chmod -R u+w "${dir}"
   255         CT_DoExecLog ALL rm -rf "${dir}"
   256         CT_DoExecLog ALL chmod ${mode} "$(dirname "${dir}")"
   257     done
   258 }
   259 
   260 # Echoes the specified string on stdout until the pipe breaks.
   261 # Doesn't fail
   262 # $1: string to echo
   263 # Usage: CT_DoYes "" |make oldconfig
   264 CT_DoYes() {
   265     yes "$1" || true
   266 }
   267 
   268 # Add the specified directory to LD_LIBRARY_PATH, and export it
   269 # If the specified patch is already present, just export
   270 # $1: path to add
   271 # $2: add as 'first' or 'last' path, 'first' is assumed if $2 is empty
   272 # Usage CT_SetLibPath /some/where/lib [first|last]
   273 CT_SetLibPath() {
   274     local path="$1"
   275     local pos="$2"
   276 
   277     case ":${LD_LIBRARY_PATH}:" in
   278         *:"${path}":*)  ;;
   279         *)  case "${pos}" in
   280                 last)
   281                     CT_DoLog DEBUG "Adding '${path}' at end of LD_LIBRARY_PATH"
   282                     LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}${path}"
   283                     ;;
   284                 first|"")
   285                     CT_DoLog DEBUG "Adding '${path}' at start of LD_LIBRARY_PATH"
   286                     LD_LIBRARY_PATH="${path}${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
   287                     ;;
   288                 *)
   289                     CT_Abort "Incorrect position '${pos}' to add '${path}' to LD_LIBRARY_PATH"
   290                     ;;
   291             esac
   292             ;;
   293     esac
   294     CT_DoLog DEBUG "==> LD_LIBRARY_PATH='${LD_LIBRARY_PATH}'"
   295     export LD_LIBRARY_PATH
   296 }
   297 
   298 # Get the file name extension of a component
   299 # Usage: CT_GetFileExtension <component_name-component_version> [extension]
   300 # If found, echoes the extension to stdout
   301 # If not found, echoes nothing on stdout.
   302 CT_GetFileExtension() {
   303     local ext
   304     local file="$1"
   305     shift
   306     local first_ext="$1"
   307 
   308     # we need to also check for an empty extension for those very
   309     # peculiar components that don't have one (such as sstrip from
   310     # buildroot).
   311     for ext in ${first_ext} .tar.gz .tar.bz2 .tgz .tar ''; do
   312         if [ -f "${CT_TARBALLS_DIR}/${file}${ext}" ]; then
   313             echo "${ext}"
   314             break
   315         fi
   316     done
   317 
   318     return 0
   319 }
   320 
   321 # Download an URL using wget
   322 # Usage: CT_DoGetFileWget <URL>
   323 CT_DoGetFileWget() {
   324     # Need to return true because it is legitimate to not find the tarball at
   325     # some of the provided URLs (think about snapshots, different layouts for
   326     # different gcc versions, etc...)
   327     # Some (very old!) FTP server might not support the passive mode, thus
   328     # retry without
   329     # With automated download as we are doing, it can be very dangerous to use
   330     # -c to continue the downloads. It's far better to simply overwrite the
   331     # destination file
   332     # Some company networks have firewalls to connect to the internet, but it's
   333     # not easy to detect them, and wget does not timeout by default while
   334     # connecting, so force a global ${CT_CONNECT_TIMEOUT}-second timeout.
   335     CT_DoExecLog ALL wget -T ${CT_CONNECT_TIMEOUT} -nc --progress=dot:binary --tries=3 --passive-ftp "$1"    \
   336     || CT_DoExecLog ALL wget -T ${CT_CONNECT_TIMEOUT} -nc --progress=dot:binary --tries=3 "$1"               \
   337     || true
   338 }
   339 
   340 # Download an URL using curl
   341 # Usage: CT_DoGetFileCurl <URL>
   342 CT_DoGetFileCurl() {
   343     # Note: comments about wget method (above) are also valid here
   344     # Plus: no good progress indicator is available with curl,
   345     #       so, be silent.
   346     CT_DoExecLog ALL curl -s --ftp-pasv -O --retry 3 "$1" --connect-timeout ${CT_CONNECT_TIMEOUT}    \
   347     || CT_DoExecLog ALL curl -s -O --retry 3 "$1" --connect-timeout ${CT_CONNECT_TIMEOUT}            \
   348     || true
   349 }
   350 
   351 _wget=$(CT_Which wget)
   352 _curl=$(CT_Which curl)
   353 # Wrapper function to call one of curl or wget
   354 # Usage: CT_DoGetFile <URL>
   355 CT_DoGetFile() {
   356     case "${_wget},${_curl}" in
   357         ,)  CT_Abort "Could find neither wget nor curl";;
   358         ,*) CT_DoGetFileCurl "$1";;
   359         *)  CT_DoGetFileWget "$1";;
   360     esac
   361 }
   362 
   363 # This function tries to retrieve a tarball form a local directory
   364 # Usage: CT_GetLocal <basename> [.extension]
   365 CT_GetLocal() {
   366     local basename="$1"
   367     local first_ext="$2"
   368     local ext
   369 
   370     # Do we already have it in *our* tarballs dir?
   371     ext=$(CT_GetFileExtension "${basename}" ${first_ext})
   372     if [ -n "${ext}" ]; then
   373         CT_DoLog DEBUG "Already have '${basename}'"
   374         return 0
   375     fi
   376 
   377     if [ -n "${CT_LOCAL_TARBALLS_DIR}" ]; then
   378         CT_DoLog DEBUG "Trying to retrieve an already downloaded copy of '${basename}'"
   379         # We'd rather have a bzip2'ed tarball, then gzipped tarball, plain tarball,
   380         # or, as a failover, a file without extension.
   381         for ext in ${first_ext} .tar.bz2 .tar.gz .tgz .tar ''; do
   382             CT_DoLog DEBUG "Trying '${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}'"
   383             if [ -r "${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}" -a \
   384                  "${CT_FORCE_DOWNLOAD}" != "y" ]; then
   385                 CT_DoLog DEBUG "Got '${basename}' from local storage"
   386                 CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}" "${CT_TARBALLS_DIR}/${basename}${ext}"
   387                 return 0
   388             fi
   389         done
   390     fi
   391     return 1
   392 }
   393 
   394 # This function saves the specified to local storage if possible,
   395 # and if so, symlinks it for later usage
   396 # Usage: CT_SaveLocal </full/path/file.name>
   397 CT_SaveLocal() {
   398     local file="$1"
   399     local basename="${file##*/}"
   400 
   401     if [ "${CT_SAVE_TARBALLS}" = "y" ]; then
   402         CT_DoLog EXTRA "Saving '${basename}' to local storage"
   403         # The file may already exist if downloads are forced: remove it first
   404         CT_DoExecLog ALL rm -f "${CT_LOCAL_TARBALLS_DIR}/${basename}"
   405         CT_DoExecLog ALL mv -f "${file}" "${CT_LOCAL_TARBALLS_DIR}"
   406         CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${basename}" "${file}"
   407     fi
   408 }
   409 
   410 # Download the file from one of the URLs passed as argument
   411 # Usage: CT_GetFile <basename> [.extension] <url> [url ...]
   412 CT_GetFile() {
   413     local ext
   414     local url URLS LAN_URLS
   415     local file="$1"
   416     local first_ext
   417     shift
   418     # If next argument starts with a dot, then this is not an URL,
   419     # and we can consider that it is a preferred extension.
   420     case "$1" in
   421         .*) first_ext="$1"
   422             shift
   423             ;;
   424     esac
   425 
   426     # Does it exist localy?
   427     CT_GetLocal "${file}" ${first_ext} && return 0 || true
   428     # No, it does not...
   429 
   430     # Are downloads allowed ?
   431     CT_TestAndAbort "File '${file}' not present locally, and downloads are not allowed" "${CT_FORBID_DOWNLOAD}" = "y"
   432 
   433     # Try to retrieve the file
   434     CT_DoLog EXTRA "Retrieving '${file}'"
   435     CT_Pushd "${CT_TARBALLS_DIR}"
   436 
   437     URLS="$@"
   438 
   439     # Add URLs on the LAN mirror
   440     LAN_URLS=
   441     if [ "${CT_USE_MIRROR}" = "y" ]; then
   442         CT_TestOrAbort "Please set the mirror base URL" -n "${CT_MIRROR_BASE_URL}"
   443         LAN_URLS="${LAN_URLS} ${CT_MIRROR_BASE_URL}/${file%-*}"
   444         LAN_URLS="${LAN_URLS} ${CT_MIRROR_BASE_URL}"
   445 
   446         if [ "${CT_PREFER_MIRROR}" = "y" ]; then
   447             CT_DoLog DEBUG "Pre-pending LAN mirror URLs"
   448             URLS="${LAN_URLS} ${URLS}"
   449         else
   450             CT_DoLog DEBUG "Appending LAN mirror URLs"
   451             URLS="${URLS} ${LAN_URLS}"
   452         fi
   453     fi
   454 
   455     # Scan all URLs in turn, and try to grab a tarball from there
   456     for ext in ${first_ext} .tar.bz2 .tar.gz .tgz .tar ''; do
   457         # Try all urls in turn
   458         for url in ${URLS}; do
   459             CT_DoLog DEBUG "Trying '${url}/${file}${ext}'"
   460             CT_DoGetFile "${url}/${file}${ext}"
   461             if [ -f "${file}${ext}" ]; then
   462                 CT_DoLog DEBUG "Got '${file}' from the Internet"
   463                 CT_SaveLocal "${CT_TARBALLS_DIR}/${file}${ext}"
   464                 return 0
   465             fi
   466         done
   467     done
   468     CT_Popd
   469 
   470     CT_Abort "Could not retrieve '${file}'."
   471 }
   472 
   473 # Checkout from CVS, and build the associated tarball
   474 # The tarball will be called ${basename}.tar.bz2
   475 # Prerequisite: either the server does not require password,
   476 # or the user must already be logged in.
   477 # 'tag' is the tag to retrieve. Must be specified, but can be empty.
   478 # If dirname is specified, then module will be renamed to dirname
   479 # prior to building the tarball.
   480 # Usage: CT_GetCVS <basename> <url> <module> <tag> [dirname]
   481 CT_GetCVS() {
   482     local basename="$1"
   483     local uri="$2"
   484     local module="$3"
   485     local tag="${4:+-r ${4}}"
   486     local dirname="$5"
   487     local tmp_dir
   488 
   489     # Does it exist localy?
   490     CT_GetLocal "${basename}" && return 0 || true
   491     # No, it does not...
   492 
   493     # Are downloads allowed ?
   494     CT_TestAndAbort "File '${basename}' not present locally, and downloads are not allowed" "${CT_FORBID_DOWNLOAD}" = "y"
   495 
   496     CT_DoLog EXTRA "Retrieving '${basename}'"
   497 
   498     CT_MktempDir tmp_dir
   499     CT_Pushd "${tmp_dir}"
   500 
   501     CT_DoExecLog ALL cvs -z 9 -d "${uri}" co -P ${tag} "${module}"
   502     [ -n "${dirname}" ] && CT_DoExecLog ALL mv "${module}" "${dirname}"
   503     CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${dirname:-${module}}"
   504     CT_SaveLocal "${CT_TARBALLS_DIR}/${basename}.tar.bz2"
   505 
   506     CT_Popd
   507     CT_DoExecLog ALL rm -rf "${tmp_dir}"
   508 }
   509 
   510 # Check out from SVN, and build the associated tarball
   511 # The tarball will be called ${basename}.tar.bz2
   512 # Prerequisite: either the server does not require password,
   513 # or the user must already be logged in.
   514 # 'rev' is the revision to retrieve
   515 # Usage: CT_GetSVN <basename> <url> [rev]
   516 CT_GetSVN() {
   517     local basename="$1"
   518     local uri="$2"
   519     local rev="$3"
   520 
   521     # Does it exist localy?
   522     CT_GetLocal "${basename}" && return 0 || true
   523     # No, it does not...
   524 
   525     # Are downloads allowed ?
   526     CT_TestAndAbort "File '${basename}' not present locally, and downloads are not allowed" "${CT_FORBID_DOWNLOAD}" = "y"
   527 
   528     CT_DoLog EXTRA "Retrieving '${basename}'"
   529 
   530     CT_MktempDir tmp_dir
   531     CT_Pushd "${tmp_dir}"
   532 
   533     CT_DoExecLog ALL svn export ${rev:+-r ${rev}} "${uri}" "${basename}"
   534     CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${basename}"
   535     CT_SaveLocal "${CT_TARBALLS_DIR}/${basename}.tar.bz2"
   536 
   537     CT_Popd
   538     CT_DoExecLog ALL rm -rf "${tmp_dir}"
   539 }
   540 
   541 # Extract a tarball
   542 # Some tarballs need to be extracted in specific places. Eg.: glibc addons
   543 # must be extracted in the glibc directory; uCLibc locales must be extracted
   544 # in the extra/locale sub-directory of uClibc. This is taken into account
   545 # by the caller, that did a 'cd' into the correct path before calling us
   546 # and sets nochdir to 'nochdir'.
   547 # Usage: CT_Extract <basename> [nochdir]
   548 CT_Extract() {
   549     local basename="$1"
   550     local nochdir="$2"
   551     local ext=$(CT_GetFileExtension "${basename}")
   552     CT_TestAndAbort "'${basename}' not found in '${CT_TARBALLS_DIR}'" -z "${ext}"
   553     local full_file="${CT_TARBALLS_DIR}/${basename}${ext}"
   554 
   555     # Check if already extracted
   556     if [ -e "${CT_SRC_DIR}/.${basename}.extracted" ]; then
   557         CT_DoLog DEBUG "Already extracted '${basename}'"
   558         return 0
   559     fi
   560 
   561     [ "${nochdir}" = "nochdir" ] || CT_Pushd "${CT_SRC_DIR}"
   562 
   563     CT_DoLog EXTRA "Extracting '${basename}'"
   564     case "${ext}" in
   565         .tar.bz2)     CT_DoExecLog ALL tar xvjf "${full_file}";;
   566         .tar.gz|.tgz) CT_DoExecLog ALL tar xvzf "${full_file}";;
   567         .tar)         CT_DoExecLog ALL tar xvf  "${full_file}";;
   568         *)            CT_Abort "Don't know how to handle '${basename}${ext}': unknown extension" ;;
   569     esac
   570 
   571     # Some tarballs have read-only files... :-(
   572     # Because of nochdir, we don't know where we are, so chmod all
   573     # the src tree
   574     CT_DoExecLog DEBUG chmod -R u+w "${CT_SRC_DIR}"
   575 
   576     CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${basename}.extracted"
   577 
   578     [ "${nochdir}" = "nochdir" ] || CT_Popd
   579 }
   580 
   581 # Patches the specified component
   582 # Usage: CT_Patch <basename> [nochdir]
   583 CT_Patch() {
   584     local basename="$1"
   585     local nochdir="$2"
   586     local base_file="${basename%%-*}"
   587     local ver_file="${basename#*-}"
   588     local official_patch_dir
   589     local custom_patch_dir
   590 
   591     # Check if already patched
   592     if [ -e "${CT_SRC_DIR}/.${basename}.patched" ]; then
   593         CT_DoLog DEBUG "Already patched '${basename}'"
   594         return 0
   595     fi
   596 
   597     # Check if already partially patched
   598     if [ -e "${CT_SRC_DIR}/.${basename}.patching" ]; then
   599         CT_DoLog ERROR "The '${basename}' sources were partially patched."
   600         CT_DoLog ERROR "Please remove first:"
   601         CT_DoLog ERROR " - the source dir for '${basename}', in '${CT_SRC_DIR}'"
   602         CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${basename}.extracted'"
   603         CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${basename}.patching'"
   604         CT_Abort "I'll stop now to avoid any carnage..."
   605     fi
   606     touch "${CT_SRC_DIR}/.${basename}.patching"
   607 
   608     [ "${nochdir}" = "nochdir" ] || CT_Pushd "${CT_SRC_DIR}/${basename}"
   609 
   610     CT_DoLog EXTRA "Patching '${basename}'"
   611 
   612     official_patch_dir=
   613     custom_patch_dir=
   614     [ "${CT_CUSTOM_PATCH_ONLY}" = "y" ] || official_patch_dir="${CT_LIB_DIR}/patches/${base_file}/${ver_file}"
   615     [ "${CT_CUSTOM_PATCH}" = "y" ] && custom_patch_dir="${CT_CUSTOM_PATCH_DIR}/${base_file}/${ver_file}"
   616     for patch_dir in "${official_patch_dir}" "${custom_patch_dir}"; do
   617         if [ -n "${patch_dir}" -a -d "${patch_dir}" ]; then
   618             for p in "${patch_dir}"/*.patch; do
   619                 if [ -f "${p}" ]; then
   620                     CT_DoLog DEBUG "Applying patch '${p}'"
   621                     CT_DoExecLog ALL patch -g0 -F1 -p1 -f <"${p}"
   622                     CT_TestAndAbort "Failed while applying patch file '${p}'" ${PIPESTATUS[0]} -ne 0
   623                 fi
   624             done
   625         fi
   626     done
   627 
   628     if [ "${CT_OVERIDE_CONFIG_GUESS_SUB}" = "y" ]; then
   629         CT_DoLog ALL "Overiding config.guess and config.sub"
   630         for cfg in config_guess config_sub; do
   631             eval ${cfg}="${CT_LIB_DIR}/scripts/${cfg/_/.}"
   632             [ -e "${CT_TOP_DIR}/scripts/${cfg/_/.}" ] && eval ${cfg}="${CT_TOP_DIR}/scripts/${cfg/_/.}"
   633             # Can't use CT_DoExecLog because of the '{} \;' to be passed un-mangled to find
   634             find . -type f -name "${cfg/_/.}" -exec cp -v "${!cfg}" {} \; |CT_DoLog ALL
   635         done
   636     fi
   637 
   638     CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/.${basename}.patching"
   639     CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${basename}.patched"
   640 
   641     [ "${nochdir}" = "nochdir" ] || CT_Popd
   642 }
   643 
   644 # Two wrappers to call config.(guess|sub) either from CT_TOP_DIR or CT_LIB_DIR.
   645 # Those from CT_TOP_DIR, if they exist, will be be more recent than those from CT_LIB_DIR.
   646 CT_DoConfigGuess() {
   647     if [ -x "${CT_TOP_DIR}/scripts/config.guess" ]; then
   648         "${CT_TOP_DIR}/scripts/config.guess"
   649     else
   650         "${CT_LIB_DIR}/scripts/config.guess"
   651     fi
   652 }
   653 
   654 CT_DoConfigSub() {
   655     if [ -x "${CT_TOP_DIR}/scripts/config.sub" ]; then
   656         "${CT_TOP_DIR}/scripts/config.sub" "$@"
   657     else
   658         "${CT_LIB_DIR}/scripts/config.sub" "$@"
   659     fi
   660 }
   661 
   662 # Compute the target tuple from what is provided by the user
   663 # Usage: CT_DoBuildTargetTuple
   664 # In fact this function takes the environment variables to build the target
   665 # tuple. It is needed both by the normal build sequence, as well as the
   666 # sample saving sequence.
   667 CT_DoBuildTargetTuple() {
   668     # Set the endianness suffix, and the default endianness gcc option
   669     case "${CT_ARCH_BE},${CT_ARCH_LE}" in
   670         y,) target_endian_eb=eb
   671             target_endian_el=
   672             CT_ARCH_ENDIAN_CFLAG="-mbig-endian"
   673             CT_ARCH_ENDIAN_LDFLAG="-EB"
   674             ;;
   675         ,y) target_endian_eb=
   676             target_endian_el=el
   677             CT_ARCH_ENDIAN_CFLAG="-mlittle-endian"
   678             CT_ARCH_ENDIAN_LDFLAG="-EL"
   679             ;;
   680     esac
   681 
   682     # Build the default architecture tuple part
   683     CT_TARGET_ARCH="${CT_ARCH}"
   684 
   685     # Set defaults for the system part of the tuple. Can be overriden
   686     # by architecture-specific values.
   687     case "${CT_LIBC}" in
   688         none)   CT_TARGET_SYS=elf;;
   689         *glibc) CT_TARGET_SYS=gnu;;
   690         uClibc) CT_TARGET_SYS=uclibc;;
   691     esac
   692 
   693     # Transform the ARCH into a kernel-understandable ARCH
   694     CT_KERNEL_ARCH="${CT_ARCH}"
   695 
   696     # Set the default values for ARCH, ABI, CPU, TUNE, FPU and FLOAT
   697     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
   698     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
   699     [ "${CT_ARCH_ARCH}"     ] && { CT_ARCH_ARCH_CFLAG="-march=${CT_ARCH_ARCH}";  CT_ARCH_WITH_ARCH="--with-arch=${CT_ARCH_ARCH}"; }
   700     [ "${CT_ARCH_ABI}"      ] && { CT_ARCH_ABI_CFLAG="-mabi=${CT_ARCH_ABI}";     CT_ARCH_WITH_ABI="--with-abi=${CT_ARCH_ABI}";    }
   701     [ "${CT_ARCH_CPU}"      ] && { CT_ARCH_CPU_CFLAG="-mcpu=${CT_ARCH_CPU}";     CT_ARCH_WITH_CPU="--with-cpu=${CT_ARCH_CPU}";    }
   702     [ "${CT_ARCH_TUNE}"     ] && { CT_ARCH_TUNE_CFLAG="-mtune=${CT_ARCH_TUNE}";  CT_ARCH_WITH_TUNE="--with-tune=${CT_ARCH_TUNE}"; }
   703     [ "${CT_ARCH_FPU}"      ] && { CT_ARCH_FPU_CFLAG="-mfpu=${CT_ARCH_FPU}";     CT_ARCH_WITH_FPU="--with-fpu=${CT_ARCH_FPU}";    }
   704     [ "${CT_ARCH_FLOAT_SW}" ] && { CT_ARCH_FLOAT_CFLAG="-msoft-float";           CT_ARCH_WITH_FLOAT="--with-float=soft";          }
   705 
   706     # Build the default kernel tuple part
   707     CT_TARGET_KERNEL="${CT_KERNEL}"
   708 
   709     # Overide the default values with the components specific settings
   710     CT_DoArchTupleValues
   711     CT_DoKernelTupleValues
   712 
   713     # Finish the target tuple construction
   714     CT_TARGET="${CT_TARGET_ARCH}-${CT_TARGET_VENDOR:-unknown}-${CT_TARGET_KERNEL}${CT_TARGET_KERNEL:+-}${CT_TARGET_SYS}"
   715 
   716     # Sanity checks
   717     __sed_alias=""
   718     if [ -n "${CT_TARGET_ALIAS_SED_EXPR}" ]; then
   719         __sed_alias=$(echo "${CT_TARGET}" |sed -r -e "${CT_TARGET_ALIAS_SED_EXPR}")
   720     fi
   721     case ":${CT_TARGET_VENDOR}:${CT_TARGET_ALIAS}:${__sed_alias}:" in
   722       :*" "*:*:*:) CT_Abort "Don't use spaces in the vendor string, it breaks things.";;
   723       :*"-"*:*:*:) CT_Abort "Don't use dashes in the vendor string, it breaks things.";;
   724       :*:*" "*:*:) CT_Abort "Don't use spaces in the target alias, it breaks things.";;
   725       :*:*:*" "*:) CT_Abort "Don't use spaces in the target sed transform, it breaks things.";;
   726     esac
   727 
   728     # Canonicalise it
   729     CT_TARGET=$(CT_DoConfigSub "${CT_TARGET}")
   730 
   731     # Prepare the target CFLAGS
   732     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ENDIAN_CFLAG}"
   733     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ARCH_CFLAG}"
   734     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ABI_CFLAG}"
   735     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_CPU_CFLAG}"
   736     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_TUNE_CFLAG}"
   737     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FPU_CFLAG}"
   738     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FLOAT_CFLAG}"
   739 
   740     # Now on for the target LDFLAGS
   741     CT_ARCH_TARGET_LDFLAGS="${CT_ARCH_TARGET_LDFLAGS} ${CT_ARCH_ENDIAN_LDFLAG}"
   742 }
   743 
   744 # This function does pause the build until the user strikes "Return"
   745 # Usage: CT_DoPause [optional_message]
   746 CT_DoPause() {
   747     local foo
   748     local message="${1:-Pausing for your pleasure}"
   749     CT_DoLog INFO "${message}"
   750     read -p "Press 'Enter' to continue, or Ctrl-C to stop..." foo >&6
   751     return 0
   752 }
   753 
   754 # This function saves the state of the toolchain to be able to restart
   755 # at any one point
   756 # Usage: CT_DoSaveState <next_step_name>
   757 CT_DoSaveState() {
   758 	[ "${CT_DEBUG_CT_SAVE_STEPS}" = "y" ] || return 0
   759     local state_name="$1"
   760     local state_dir="${CT_STATE_DIR}/${state_name}"
   761 
   762     # Log this to the log level required by the user
   763     CT_DoLog ${CT_LOG_LEVEL_MAX} "Saving state to restart at step '${state_name}'..."
   764 
   765     rm -rf "${state_dir}"
   766     mkdir -p "${state_dir}"
   767 
   768     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   769         y)  tar_opt=z; tar_ext=.gz;;
   770         *)  tar_opt=;  tar_ext=;;
   771     esac
   772 
   773     CT_DoLog DEBUG "  Saving environment and aliases"
   774     # We must omit shell functions, and some specific bash variables
   775     # that break when restoring the environment, later. We could do
   776     # all the processing in the awk script, but a sed is easier...
   777     set |awk '
   778               BEGIN { _p = 1; }
   779               $0~/^[^ ]+ \(\)/ { _p = 0; }
   780               _p == 1
   781               $0 == "}" { _p = 1; }
   782               ' |sed -r -e '/^BASH_(ARGC|ARGV|LINENO|SOURCE|VERSINFO)=/d;
   783                            /^(UID|EUID)=/d;
   784                            /^(FUNCNAME|GROUPS|PPID|SHELLOPTS)=/d;' >"${state_dir}/env.sh"
   785 
   786     CT_DoLog DEBUG "  Saving CT_CONFIG_DIR='${CT_CONFIG_DIR}'"
   787     CT_Pushd "${CT_CONFIG_DIR}"
   788     CT_DoExecLog DEBUG tar cv${tar_opt}f "${state_dir}/config_dir.tar${tar_ext}" .
   789     CT_Popd
   790 
   791     CT_DoLog DEBUG "  Saving CT_CC_CORE_STATIC_PREFIX_DIR='${CT_CC_CORE_STATIC_PREFIX_DIR}'"
   792     CT_Pushd "${CT_CC_CORE_STATIC_PREFIX_DIR}"
   793     CT_DoExecLog DEBUG tar cv${tar_opt}f "${state_dir}/cc_core_static_prefix_dir.tar${tar_ext}" .
   794     CT_Popd
   795 
   796     CT_DoLog DEBUG "  Saving CT_CC_CORE_SHARED_PREFIX_DIR='${CT_CC_CORE_SHARED_PREFIX_DIR}'"
   797     CT_Pushd "${CT_CC_CORE_SHARED_PREFIX_DIR}"
   798     CT_DoExecLog DEBUG tar cv${tar_opt}f "${state_dir}/cc_core_shared_prefix_dir.tar${tar_ext}" .
   799     CT_Popd
   800 
   801     CT_DoLog DEBUG "  Saving CT_PREFIX_DIR='${CT_PREFIX_DIR}'"
   802     CT_Pushd "${CT_PREFIX_DIR}"
   803     CT_DoExecLog DEBUG tar cv${tar_opt}f "${state_dir}/prefix_dir.tar${tar_ext}" --exclude '*.log' .
   804     CT_Popd
   805 
   806     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
   807         CT_DoLog DEBUG "  Saving log file"
   808         exec >/dev/null
   809         case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   810             y)  gzip -3 -c "${CT_LOG_FILE}"  >"${state_dir}/log.gz";;
   811             *)  cat "${CT_LOG_FILE}" >"${state_dir}/log";;
   812         esac
   813         exec >>"${CT_LOG_FILE}"
   814     fi
   815 }
   816 
   817 # This function restores a previously saved state
   818 # Usage: CT_DoLoadState <state_name>
   819 CT_DoLoadState(){
   820     local state_name="$1"
   821     local state_dir="${CT_STATE_DIR}/${state_name}"
   822     local old_RESTART="${CT_RESTART}"
   823     local old_STOP="${CT_STOP}"
   824 
   825     CT_TestOrAbort "The previous build did not reach the point where it could be restarted at '${CT_RESTART}'" -d "${state_dir}"
   826 
   827     # We need to do something special with the log file!
   828     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
   829         exec >"${state_dir}/tail.log"
   830     fi
   831 
   832     # Log this to the log level required by the user
   833     CT_DoLog ${CT_LOG_LEVEL_MAX} "Restoring state at step '${state_name}', as requested."
   834 
   835     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   836         y)  tar_opt=z; tar_ext=.gz;;
   837         *)  tar_opt=;  tar_ext=;;
   838     esac
   839 
   840     CT_DoLog DEBUG "  Removing previous build directories"
   841     CT_DoForceRmdir             "${CT_PREFIX_DIR}" "${CT_CC_CORE_SHARED_PREFIX_DIR}" "${CT_CC_CORE_STATIC_PREFIX_DIR}" "${CT_CONFIG_DIR}"
   842     CT_DoExecLog DEBUG mkdir -p "${CT_PREFIX_DIR}" "${CT_CC_CORE_SHARED_PREFIX_DIR}" "${CT_CC_CORE_STATIC_PREFIX_DIR}" "${CT_CONFIG_DIR}"
   843 
   844     CT_DoLog DEBUG "  Restoring CT_PREFIX_DIR='${CT_PREFIX_DIR}'"
   845     CT_Pushd "${CT_PREFIX_DIR}"
   846     CT_DoExecLog DEBUG tar xv${tar_opt}f "${state_dir}/prefix_dir.tar${tar_ext}"
   847     CT_Popd
   848 
   849     CT_DoLog DEBUG "  Restoring CT_CC_CORE_SHARED_PREFIX_DIR='${CT_CC_CORE_SHARED_PREFIX_DIR}'"
   850     CT_Pushd "${CT_CC_CORE_SHARED_PREFIX_DIR}"
   851     CT_DoExecLog DEBUG tar xv${tar_opt}f "${state_dir}/cc_core_shared_prefix_dir.tar${tar_ext}"
   852     CT_Popd
   853 
   854     CT_DoLog DEBUG "  Restoring CT_CC_CORE_STATIC_PREFIX_DIR='${CT_CC_CORE_STATIC_PREFIX_DIR}'"
   855     CT_Pushd "${CT_CC_CORE_STATIC_PREFIX_DIR}"
   856     CT_DoExecLog DEBUG tar xv${tar_opt}f "${state_dir}/cc_core_static_prefix_dir.tar${tar_ext}"
   857     CT_Popd
   858 
   859     CT_DoLog DEBUG "  Restoring CT_CONFIG_DIR='${CT_CONFIG_DIR}'"
   860     CT_Pushd "${CT_CONFIG_DIR}"
   861     CT_DoExecLog DEBUG tar xv${tar_opt}f "${state_dir}/config_dir.tar${tar_ext}"
   862     CT_Popd
   863 
   864     # Restore the environment, discarding any error message
   865     # (for example, read-only bash internals)
   866     CT_DoLog DEBUG "  Restoring environment"
   867     . "${state_dir}/env.sh" >/dev/null 2>&1 || true
   868 
   869     # Restore the new RESTART and STOP steps
   870     CT_RESTART="${old_RESTART}"
   871     CT_STOP="${old_STOP}"
   872     unset old_stop old_restart
   873 
   874     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
   875         CT_DoLog DEBUG "  Restoring log file"
   876         exec >/dev/null
   877         case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   878             y)  zcat "${state_dir}/log.gz" >"${CT_LOG_FILE}";;
   879             *)  cat "${state_dir}/log" >"${CT_LOG_FILE}";;
   880         esac
   881         cat "${state_dir}/tail.log" >>"${CT_LOG_FILE}"
   882         exec >>"${CT_LOG_FILE}"
   883         rm -f "${state_dir}/tail.log"
   884     fi
   885 }