scripts/functions
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Thu Jun 11 21:47:19 2009 +0000 (2009-06-11)
branch1.4
changeset 1451 25d050084e98
parent 1294 6fe8df60cfa4
child 1391 3c28b9f917d2
permissions -rw-r--r--
populate: fix installing dynamic linker 'ld.so'

The dynamic linker, ld.so, needs the execute bit to be set.
Detect tht the library being installed is in fact ld.so and
install it with 0755 instead of 0644.

Fix detecting src == dst.

Use a simpler command to copy src -> dst.

Also change echo to printf, get rid of 'echo -n', which is
highly non-portable.


-------- diffstat follows --------
/trunk/scripts/populate.in | 76 43 33 0 +++++++++++++++++++++++++++++-----------------------
1 file changed, 43 insertions(+), 33 deletions(-)
(transplanted from d7ddcb75e0f703e2ba6d17169167356389224870)
     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         echo "${@}"
    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 # 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     # we need to also check for an empty extension for those very
   279     # peculiar components that don't have one (such as sstrip from
   280     # buildroot).
   281     for ext in ${first_ext} .tar.gz .tar.bz2 .tgz .tar ''; do
   282         if [ -f "${CT_TARBALLS_DIR}/${file}${ext}" ]; then
   283             echo "${ext}"
   284             break
   285         fi
   286     done
   287 
   288     return 0
   289 }
   290 
   291 # Download an URL using wget
   292 # Usage: CT_DoGetFileWget <URL>
   293 CT_DoGetFileWget() {
   294     # Need to return true because it is legitimate to not find the tarball at
   295     # some of the provided URLs (think about snapshots, different layouts for
   296     # different gcc versions, etc...)
   297     # Some (very old!) FTP server might not support the passive mode, thus
   298     # retry without
   299     # With automated download as we are doing, it can be very dangerous to use
   300     # -c to continue the downloads. It's far better to simply overwrite the
   301     # destination file
   302     # Some company networks have firewalls to connect to the internet, but it's
   303     # not easy to detect them, and wget does not timeout by default while
   304     # connecting, so force a global ${CT_CONNECT_TIMEOUT}-second timeout.
   305     CT_DoExecLog ALL wget -T ${CT_CONNECT_TIMEOUT} -nc --progress=dot:binary --tries=3 --passive-ftp "$1"    \
   306     || CT_DoExecLog ALL wget -T ${CT_CONNECT_TIMEOUT} -nc --progress=dot:binary --tries=3 "$1"               \
   307     || true
   308 }
   309 
   310 # Download an URL using curl
   311 # Usage: CT_DoGetFileCurl <URL>
   312 CT_DoGetFileCurl() {
   313     # Note: comments about wget method (above) are also valid here
   314     # Plus: no good progress indicator is available with curl,
   315     #       so, be silent.
   316     CT_DoExecLog ALL curl -s --ftp-pasv -O --retry 3 "$1" --connect-timeout ${CT_CONNECT_TIMEOUT}    \
   317     || CT_DoExecLog ALL curl -s -O --retry 3 "$1" --connect-timeout ${CT_CONNECT_TIMEOUT}            \
   318     || true
   319 }
   320 
   321 _wget=$(CT_Which wget)
   322 _curl=$(CT_Which curl)
   323 # Wrapper function to call one of curl or wget
   324 # Usage: CT_DoGetFile <URL>
   325 CT_DoGetFile() {
   326     case "${_wget},${_curl}" in
   327         ,)  CT_Abort "Could find neither wget nor curl";;
   328         ,*) CT_DoGetFileCurl "$1";;
   329         *)  CT_DoGetFileWget "$1";;
   330     esac
   331 }
   332 
   333 # This function tries to retrieve a tarball form a local directory
   334 # Usage: CT_GetLocal <basename> [.extension]
   335 CT_GetLocal() {
   336     local basename="$1"
   337     local first_ext="$2"
   338     local ext
   339 
   340     # Do we already have it in *our* tarballs dir?
   341     ext=$(CT_GetFileExtension "${basename}" ${first_ext})
   342     if [ -n "${ext}" ]; then
   343         CT_DoLog DEBUG "Already have '${basename}'"
   344         return 0
   345     fi
   346 
   347     if [ -n "${CT_LOCAL_TARBALLS_DIR}" ]; then
   348         CT_DoLog DEBUG "Trying to retrieve an already downloaded copy of '${basename}'"
   349         # We'd rather have a bzip2'ed tarball, then gzipped tarball, plain tarball,
   350         # or, as a failover, a file without extension.
   351         for ext in ${first_ext} .tar.bz2 .tar.gz .tgz .tar ''; do
   352             CT_DoLog DEBUG "Trying '${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}'"
   353             if [ -r "${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}" -a \
   354                  "${CT_FORCE_DOWNLOAD}" != "y" ]; then
   355                 CT_DoLog DEBUG "Got '${basename}' from local storage"
   356                 CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}" "${CT_TARBALLS_DIR}/${basename}${ext}"
   357                 return 0
   358             fi
   359         done
   360     fi
   361     return 1
   362 }
   363 
   364 # This function saves the specified to local storage if possible,
   365 # and if so, symlinks it for later usage
   366 # Usage: CT_SaveLocal </full/path/file.name>
   367 CT_SaveLocal() {
   368     local file="$1"
   369     local basename="${file##*/}"
   370 
   371     if [ "${CT_SAVE_TARBALLS}" = "y" ]; then
   372         CT_DoLog EXTRA "Saving '${basename}' to local storage"
   373         # The file may already exist if downloads are forced: remove it first
   374         CT_DoExecLog ALL rm -f "${CT_LOCAL_TARBALLS_DIR}/${basename}"
   375         CT_DoExecLog ALL mv -f "${file}" "${CT_LOCAL_TARBALLS_DIR}"
   376         CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${basename}" "${file}"
   377     fi
   378 }
   379 
   380 # Download the file from one of the URLs passed as argument
   381 # Usage: CT_GetFile <basename> [.extension] <url> [url ...]
   382 CT_GetFile() {
   383     local ext
   384     local url URLS LAN_URLS
   385     local file="$1"
   386     local first_ext
   387     shift
   388     # If next argument starts with a dot, then this is not an URL,
   389     # and we can consider that it is a preferred extension.
   390     case "$1" in
   391         .*) first_ext="$1"
   392             shift
   393             ;;
   394     esac
   395 
   396     # Does it exist localy?
   397     CT_GetLocal "${file}" ${first_ext} && return 0 || true
   398     # No, it does not...
   399 
   400     # Are downloads allowed ?
   401     CT_TestAndAbort "File '${file}' not present locally, and downloads are not allowed" "${CT_FORBID_DOWNLOAD}" = "y"
   402 
   403     # Try to retrieve the file
   404     CT_DoLog EXTRA "Retrieving '${file}'"
   405     CT_Pushd "${CT_TARBALLS_DIR}"
   406 
   407     URLS="$@"
   408 
   409     # Add URLs on the LAN mirror
   410     LAN_URLS=
   411     if [ "${CT_USE_MIRROR}" = "y" ]; then
   412         CT_TestOrAbort "Please set the mirror base URL" -n "${CT_MIRROR_BASE_URL}"
   413         LAN_URLS="${LAN_URLS} ${CT_MIRROR_BASE_URL}/${file%-*}"
   414         LAN_URLS="${LAN_URLS} ${CT_MIRROR_BASE_URL}"
   415 
   416         if [ "${CT_PREFER_MIRROR}" = "y" ]; then
   417             CT_DoLog DEBUG "Pre-pending LAN mirror URLs"
   418             URLS="${LAN_URLS} ${URLS}"
   419         else
   420             CT_DoLog DEBUG "Appending LAN mirror URLs"
   421             URLS="${URLS} ${LAN_URLS}"
   422         fi
   423     fi
   424 
   425     # Scan all URLs in turn, and try to grab a tarball from there
   426     for ext in ${first_ext} .tar.bz2 .tar.gz .tgz .tar ''; do
   427         # Try all urls in turn
   428         for url in ${URLS}; do
   429             CT_DoLog DEBUG "Trying '${url}/${file}${ext}'"
   430             CT_DoGetFile "${url}/${file}${ext}"
   431             if [ -f "${file}${ext}" ]; then
   432                 CT_DoLog DEBUG "Got '${file}' from the Internet"
   433                 CT_SaveLocal "${CT_TARBALLS_DIR}/${file}${ext}"
   434                 return 0
   435             fi
   436         done
   437     done
   438     CT_Popd
   439 
   440     CT_Abort "Could not retrieve '${file}'."
   441 }
   442 
   443 # Checkout from CVS, and build the associated tarball
   444 # The tarball will be called ${basename}.tar.bz2
   445 # Prerequisite: either the server does not require password,
   446 # or the user must already be logged in.
   447 # 'tag' is the tag to retrieve. Must be specified, but can be empty.
   448 # If dirname is specified, then module will be renamed to dirname
   449 # prior to building the tarball.
   450 # Usage: CT_GetCVS <basename> <url> <module> <tag> [dirname]
   451 CT_GetCVS() {
   452     local basename="$1"
   453     local uri="$2"
   454     local module="$3"
   455     local tag="${4:+-r ${4}}"
   456     local dirname="$5"
   457     local tmp_dir
   458 
   459     # Does it exist localy?
   460     CT_GetLocal "${basename}" && return 0 || true
   461     # No, it does not...
   462 
   463     # Are downloads allowed ?
   464     CT_TestAndAbort "File '${basename}' not present locally, and downloads are not allowed" "${CT_FORBID_DOWNLOAD}" = "y"
   465 
   466     CT_DoLog EXTRA "Retrieving '${basename}'"
   467 
   468     CT_MktempDir tmp_dir
   469     CT_Pushd "${tmp_dir}"
   470 
   471     CT_DoExecLog ALL cvs -z 9 -d "${uri}" co -P ${tag} "${module}"
   472     [ -n "${dirname}" ] && CT_DoExecLog ALL mv "${module}" "${dirname}"
   473     CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${dirname:-${module}}"
   474     CT_SaveLocal "${CT_TARBALLS_DIR}/${basename}.tar.bz2"
   475 
   476     CT_Popd
   477     CT_DoExecLog ALL rm -rf "${tmp_dir}"
   478 }
   479 
   480 # Check out from SVN, and build the associated tarball
   481 # The tarball will be called ${basename}.tar.bz2
   482 # Prerequisite: either the server does not require password,
   483 # or the user must already be logged in.
   484 # 'rev' is the revision to retrieve
   485 # Usage: CT_GetSVN <basename> <url> [rev]
   486 CT_GetSVN() {
   487     local basename="$1"
   488     local uri="$2"
   489     local rev="$3"
   490 
   491     # Does it exist localy?
   492     CT_GetLocal "${basename}" && return 0 || true
   493     # No, it does not...
   494 
   495     # Are downloads allowed ?
   496     CT_TestAndAbort "File '${basename}' not present locally, and downloads are not allowed" "${CT_FORBID_DOWNLOAD}" = "y"
   497 
   498     CT_DoLog EXTRA "Retrieving '${basename}'"
   499 
   500     CT_MktempDir tmp_dir
   501     CT_Pushd "${tmp_dir}"
   502 
   503     CT_DoExecLog ALL svn export ${rev:+-r ${rev}} "${uri}" "${basename}"
   504     CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${basename}"
   505     CT_SaveLocal "${CT_TARBALLS_DIR}/${basename}.tar.bz2"
   506 
   507     CT_Popd
   508     CT_DoExecLog ALL rm -rf "${tmp_dir}"
   509 }
   510 
   511 # Extract a tarball
   512 # Some tarballs need to be extracted in specific places. Eg.: glibc addons
   513 # must be extracted in the glibc directory; uCLibc locales must be extracted
   514 # in the extra/locale sub-directory of uClibc. This is taken into account
   515 # by the caller, that did a 'cd' into the correct path before calling us
   516 # and sets nochdir to 'nochdir'.
   517 # Usage: CT_Extract <basename> [nochdir]
   518 CT_Extract() {
   519     local basename="$1"
   520     local nochdir="$2"
   521     local ext=$(CT_GetFileExtension "${basename}")
   522     CT_TestAndAbort "'${basename}' not found in '${CT_TARBALLS_DIR}'" -z "${ext}"
   523     local full_file="${CT_TARBALLS_DIR}/${basename}${ext}"
   524 
   525     # Check if already extracted
   526     if [ -e "${CT_SRC_DIR}/.${basename}.extracted" ]; then
   527         CT_DoLog DEBUG "Already extracted '${basename}'"
   528         return 0
   529     fi
   530 
   531     [ "${nochdir}" = "nochdir" ] || CT_Pushd "${CT_SRC_DIR}"
   532 
   533     CT_DoLog EXTRA "Extracting '${basename}'"
   534     case "${ext}" in
   535         .tar.bz2)     CT_DoExecLog ALL tar xvjf "${full_file}";;
   536         .tar.gz|.tgz) CT_DoExecLog ALL tar xvzf "${full_file}";;
   537         .tar)         CT_DoExecLog ALL tar xvf  "${full_file}";;
   538         *)            CT_Abort "Don't know how to handle '${basename}${ext}': unknown extension" ;;
   539     esac
   540 
   541     # Some tarballs have read-only files... :-(
   542     # Because of nochdir, we don't know where we are, so chmod all
   543     # the src tree
   544     CT_DoExecLog DEBUG chmod -R u+w "${CT_SRC_DIR}"
   545 
   546     CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${basename}.extracted"
   547 
   548     [ "${nochdir}" = "nochdir" ] || CT_Popd
   549 }
   550 
   551 # Patches the specified component
   552 # Usage: CT_Patch <basename> [nochdir]
   553 CT_Patch() {
   554     local basename="$1"
   555     local nochdir="$2"
   556     local base_file="${basename%%-*}"
   557     local ver_file="${basename#*-}"
   558     local official_patch_dir
   559     local custom_patch_dir
   560 
   561     # Check if already patched
   562     if [ -e "${CT_SRC_DIR}/.${basename}.patched" ]; then
   563         CT_DoLog DEBUG "Already patched '${basename}'"
   564         return 0
   565     fi
   566 
   567     # Check if already partially patched
   568     if [ -e "${CT_SRC_DIR}/.${basename}.patching" ]; then
   569         CT_DoLog ERROR "The '${basename}' sources were partially patched."
   570         CT_DoLog ERROR "Please remove first:"
   571         CT_DoLog ERROR " - the source dir for '${basename}', in '${CT_SRC_DIR}'"
   572         CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${basename}.extracted'"
   573         CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${basename}.patching'"
   574         CT_Abort "I'll stop now to avoid any carnage..."
   575     fi
   576     touch "${CT_SRC_DIR}/.${basename}.patching"
   577 
   578     [ "${nochdir}" = "nochdir" ] || CT_Pushd "${CT_SRC_DIR}/${basename}"
   579 
   580     CT_DoLog EXTRA "Patching '${basename}'"
   581 
   582     official_patch_dir=
   583     custom_patch_dir=
   584     [ "${CT_CUSTOM_PATCH_ONLY}" = "y" ] || official_patch_dir="${CT_LIB_DIR}/patches/${base_file}/${ver_file}"
   585     [ "${CT_CUSTOM_PATCH}" = "y" ] && custom_patch_dir="${CT_CUSTOM_PATCH_DIR}/${base_file}/${ver_file}"
   586     for patch_dir in "${official_patch_dir}" "${custom_patch_dir}"; do
   587         if [ -n "${patch_dir}" -a -d "${patch_dir}" ]; then
   588             for p in "${patch_dir}"/*.patch; do
   589                 if [ -f "${p}" ]; then
   590                     CT_DoLog DEBUG "Applying patch '${p}'"
   591                     CT_DoExecLog ALL patch -g0 -F1 -p1 -f <"${p}"
   592                     CT_TestAndAbort "Failed while applying patch file '${p}'" ${PIPESTATUS[0]} -ne 0
   593                 fi
   594             done
   595         fi
   596     done
   597 
   598     if [ "${CT_OVERIDE_CONFIG_GUESS_SUB}" = "y" ]; then
   599         CT_DoLog ALL "Overiding config.guess and config.sub"
   600         for cfg in config_guess config_sub; do
   601             eval ${cfg}="${CT_LIB_DIR}/scripts/${cfg/_/.}"
   602             [ -e "${CT_TOP_DIR}/scripts/${cfg/_/.}" ] && eval ${cfg}="${CT_TOP_DIR}/scripts/${cfg/_/.}"
   603             # Can't use CT_DoExecLog because of the '{} \;' to be passed un-mangled to find
   604             find . -type f -name "${cfg/_/.}" -exec cp -v "${!cfg}" {} \; |CT_DoLog ALL
   605         done
   606     fi
   607 
   608     CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/.${basename}.patching"
   609     CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${basename}.patched"
   610 
   611     [ "${nochdir}" = "nochdir" ] || CT_Popd
   612 }
   613 
   614 # Two wrappers to call config.(guess|sub) either from CT_TOP_DIR or CT_LIB_DIR.
   615 # Those from CT_TOP_DIR, if they exist, will be be more recent than those from CT_LIB_DIR.
   616 CT_DoConfigGuess() {
   617     if [ -x "${CT_TOP_DIR}/scripts/config.guess" ]; then
   618         "${CT_TOP_DIR}/scripts/config.guess"
   619     else
   620         "${CT_LIB_DIR}/scripts/config.guess"
   621     fi
   622 }
   623 
   624 CT_DoConfigSub() {
   625     if [ -x "${CT_TOP_DIR}/scripts/config.sub" ]; then
   626         "${CT_TOP_DIR}/scripts/config.sub" "$@"
   627     else
   628         "${CT_LIB_DIR}/scripts/config.sub" "$@"
   629     fi
   630 }
   631 
   632 # Compute the target tuple from what is provided by the user
   633 # Usage: CT_DoBuildTargetTuple
   634 # In fact this function takes the environment variables to build the target
   635 # tuple. It is needed both by the normal build sequence, as well as the
   636 # sample saving sequence.
   637 CT_DoBuildTargetTuple() {
   638     # Set the endianness suffix, and the default endianness gcc option
   639     case "${CT_ARCH_BE},${CT_ARCH_LE}" in
   640         y,) target_endian_eb=eb
   641             target_endian_el=
   642             CT_ARCH_ENDIAN_CFLAG="-mbig-endian"
   643             CT_ARCH_ENDIAN_LDFLAG="-EB"
   644             ;;
   645         ,y) target_endian_eb=
   646             target_endian_el=el
   647             CT_ARCH_ENDIAN_CFLAG="-mlittle-endian"
   648             CT_ARCH_ENDIAN_LDFLAG="-EL"
   649             ;;
   650     esac
   651 
   652     # Build the default architecture tuple part
   653     CT_TARGET_ARCH="${CT_ARCH}"
   654 
   655     # Set defaults for the system part of the tuple. Can be overriden
   656     # by architecture-specific values.
   657     case "${CT_LIBC}" in
   658         none)   CT_TARGET_SYS=elf;;
   659         *glibc) CT_TARGET_SYS=gnu;;
   660         uClibc) CT_TARGET_SYS=uclibc;;
   661     esac
   662 
   663     # Transform the ARCH into a kernel-understandable ARCH
   664     CT_KERNEL_ARCH="${CT_ARCH}"
   665 
   666     # Set the default values for ARCH, ABI, CPU, TUNE, FPU and FLOAT
   667     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
   668     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
   669     [ "${CT_ARCH_ARCH}"     ] && { CT_ARCH_ARCH_CFLAG="-march=${CT_ARCH_ARCH}";  CT_ARCH_WITH_ARCH="--with-arch=${CT_ARCH_ARCH}"; }
   670     [ "${CT_ARCH_ABI}"      ] && { CT_ARCH_ABI_CFLAG="-mabi=${CT_ARCH_ABI}";     CT_ARCH_WITH_ABI="--with-abi=${CT_ARCH_ABI}";    }
   671     [ "${CT_ARCH_CPU}"      ] && { CT_ARCH_CPU_CFLAG="-mcpu=${CT_ARCH_CPU}";     CT_ARCH_WITH_CPU="--with-cpu=${CT_ARCH_CPU}";    }
   672     [ "${CT_ARCH_TUNE}"     ] && { CT_ARCH_TUNE_CFLAG="-mtune=${CT_ARCH_TUNE}";  CT_ARCH_WITH_TUNE="--with-tune=${CT_ARCH_TUNE}"; }
   673     [ "${CT_ARCH_FPU}"      ] && { CT_ARCH_FPU_CFLAG="-mfpu=${CT_ARCH_FPU}";     CT_ARCH_WITH_FPU="--with-fpu=${CT_ARCH_FPU}";    }
   674     [ "${CT_ARCH_FLOAT_SW}" ] && { CT_ARCH_FLOAT_CFLAG="-msoft-float";           CT_ARCH_WITH_FLOAT="--with-float=soft";          }
   675 
   676     # Build the default kernel tuple part
   677     CT_TARGET_KERNEL="${CT_KERNEL}"
   678 
   679     # Overide the default values with the components specific settings
   680     CT_DoArchTupleValues
   681     CT_DoKernelTupleValues
   682 
   683     # Finish the target tuple construction
   684     CT_TARGET="${CT_TARGET_ARCH}-${CT_TARGET_VENDOR:-unknown}-${CT_TARGET_KERNEL}${CT_TARGET_KERNEL:+-}${CT_TARGET_SYS}"
   685 
   686     # Sanity checks
   687     __sed_alias=""
   688     if [ -n "${CT_TARGET_ALIAS_SED_EXPR}" ]; then
   689         __sed_alias=$(echo "${CT_TARGET}" |sed -r -e "${CT_TARGET_ALIAS_SED_EXPR}")
   690     fi
   691     case ":${CT_TARGET_VENDOR}:${CT_TARGET_ALIAS}:${__sed_alias}:" in
   692       :*" "*:*:*:) CT_Abort "Don't use spaces in the vendor string, it breaks things.";;
   693       :*"-"*:*:*:) CT_Abort "Don't use dashes in the vendor string, it breaks things.";;
   694       :*:*" "*:*:) CT_Abort "Don't use spaces in the target alias, it breaks things.";;
   695       :*:*:*" "*:) CT_Abort "Don't use spaces in the target sed transform, it breaks things.";;
   696     esac
   697 
   698     # Canonicalise it
   699     CT_TARGET=$(CT_DoConfigSub "${CT_TARGET}")
   700 
   701     # Prepare the target CFLAGS
   702     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ENDIAN_CFLAG}"
   703     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ARCH_CFLAG}"
   704     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ABI_CFLAG}"
   705     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_CPU_CFLAG}"
   706     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_TUNE_CFLAG}"
   707     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FPU_CFLAG}"
   708     CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FLOAT_CFLAG}"
   709 
   710     # Now on for the target LDFLAGS
   711     CT_ARCH_TARGET_LDFLAGS="${CT_ARCH_TARGET_LDFLAGS} ${CT_ARCH_ENDIAN_LDFLAG}"
   712 }
   713 
   714 # This function does pause the build until the user strikes "Return"
   715 # Usage: CT_DoPause [optional_message]
   716 CT_DoPause() {
   717     local foo
   718     local message="${1:-Pausing for your pleasure}"
   719     CT_DoLog INFO "${message}"
   720     read -p "Press 'Enter' to continue, or Ctrl-C to stop..." foo >&6
   721     return 0
   722 }
   723 
   724 # This function saves the state of the toolchain to be able to restart
   725 # at any one point
   726 # Usage: CT_DoSaveState <next_step_name>
   727 CT_DoSaveState() {
   728 	[ "${CT_DEBUG_CT_SAVE_STEPS}" = "y" ] || return 0
   729     local state_name="$1"
   730     local state_dir="${CT_STATE_DIR}/${state_name}"
   731 
   732     # Log this to the log level required by the user
   733     CT_DoLog ${CT_LOG_LEVEL_MAX} "Saving state to restart at step '${state_name}'..."
   734 
   735     rm -rf "${state_dir}"
   736     mkdir -p "${state_dir}"
   737 
   738     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   739         y)  tar_opt=z; tar_ext=.gz;;
   740         *)  tar_opt=;  tar_ext=;;
   741     esac
   742 
   743     CT_DoLog DEBUG "  Saving environment and aliases"
   744     # We must omit shell functions, and some specific bash variables
   745     # that break when restoring the environment, later. We could do
   746     # all the processing in the awk script, but a sed is easier...
   747     set |awk '
   748               BEGIN { _p = 1; }
   749               $0~/^[^ ]+ \(\)/ { _p = 0; }
   750               _p == 1
   751               $0 == "}" { _p = 1; }
   752               ' |sed -r -e '/^BASH_(ARGC|ARGV|LINENO|SOURCE|VERSINFO)=/d;
   753                            /^(UID|EUID)=/d;
   754                            /^(FUNCNAME|GROUPS|PPID|SHELLOPTS)=/d;' >"${state_dir}/env.sh"
   755 
   756     CT_DoLog DEBUG "  Saving CT_CONFIG_DIR='${CT_CONFIG_DIR}'"
   757     CT_Pushd "${CT_CONFIG_DIR}"
   758     CT_DoExecLog DEBUG tar cv${tar_opt}f "${state_dir}/config_dir.tar${tar_ext}" .
   759     CT_Popd
   760 
   761     CT_DoLog DEBUG "  Saving CT_CC_CORE_STATIC_PREFIX_DIR='${CT_CC_CORE_STATIC_PREFIX_DIR}'"
   762     CT_Pushd "${CT_CC_CORE_STATIC_PREFIX_DIR}"
   763     CT_DoExecLog DEBUG tar cv${tar_opt}f "${state_dir}/cc_core_static_prefix_dir.tar${tar_ext}" .
   764     CT_Popd
   765 
   766     CT_DoLog DEBUG "  Saving CT_CC_CORE_SHARED_PREFIX_DIR='${CT_CC_CORE_SHARED_PREFIX_DIR}'"
   767     CT_Pushd "${CT_CC_CORE_SHARED_PREFIX_DIR}"
   768     CT_DoExecLog DEBUG tar cv${tar_opt}f "${state_dir}/cc_core_shared_prefix_dir.tar${tar_ext}" .
   769     CT_Popd
   770 
   771     CT_DoLog DEBUG "  Saving CT_PREFIX_DIR='${CT_PREFIX_DIR}'"
   772     CT_Pushd "${CT_PREFIX_DIR}"
   773     CT_DoExecLog DEBUG tar cv${tar_opt}f "${state_dir}/prefix_dir.tar${tar_ext}" --exclude '*.log' .
   774     CT_Popd
   775 
   776     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
   777         CT_DoLog DEBUG "  Saving log file"
   778         exec >/dev/null
   779         case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   780             y)  gzip -3 -c "${CT_LOG_FILE}"  >"${state_dir}/log.gz";;
   781             *)  cat "${CT_LOG_FILE}" >"${state_dir}/log";;
   782         esac
   783         exec >>"${CT_LOG_FILE}"
   784     fi
   785 }
   786 
   787 # This function restores a previously saved state
   788 # Usage: CT_DoLoadState <state_name>
   789 CT_DoLoadState(){
   790     local state_name="$1"
   791     local state_dir="${CT_STATE_DIR}/${state_name}"
   792     local old_RESTART="${CT_RESTART}"
   793     local old_STOP="${CT_STOP}"
   794 
   795     CT_TestOrAbort "The previous build did not reach the point where it could be restarted at '${CT_RESTART}'" -d "${state_dir}"
   796 
   797     # We need to do something special with the log file!
   798     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
   799         exec >"${state_dir}/tail.log"
   800     fi
   801 
   802     # Log this to the log level required by the user
   803     CT_DoLog ${CT_LOG_LEVEL_MAX} "Restoring state at step '${state_name}', as requested."
   804 
   805     case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   806         y)  tar_opt=z; tar_ext=.gz;;
   807         *)  tar_opt=;  tar_ext=;;
   808     esac
   809 
   810     CT_DoLog DEBUG "  Removing previous build directories"
   811     CT_DoForceRmdir             "${CT_PREFIX_DIR}" "${CT_CC_CORE_SHARED_PREFIX_DIR}" "${CT_CC_CORE_STATIC_PREFIX_DIR}" "${CT_CONFIG_DIR}"
   812     CT_DoExecLog DEBUG mkdir -p "${CT_PREFIX_DIR}" "${CT_CC_CORE_SHARED_PREFIX_DIR}" "${CT_CC_CORE_STATIC_PREFIX_DIR}" "${CT_CONFIG_DIR}"
   813 
   814     CT_DoLog DEBUG "  Restoring CT_PREFIX_DIR='${CT_PREFIX_DIR}'"
   815     CT_Pushd "${CT_PREFIX_DIR}"
   816     CT_DoExecLog DEBUG tar xv${tar_opt}f "${state_dir}/prefix_dir.tar${tar_ext}"
   817     CT_Popd
   818 
   819     CT_DoLog DEBUG "  Restoring CT_CC_CORE_SHARED_PREFIX_DIR='${CT_CC_CORE_SHARED_PREFIX_DIR}'"
   820     CT_Pushd "${CT_CC_CORE_SHARED_PREFIX_DIR}"
   821     CT_DoExecLog DEBUG tar xv${tar_opt}f "${state_dir}/cc_core_shared_prefix_dir.tar${tar_ext}"
   822     CT_Popd
   823 
   824     CT_DoLog DEBUG "  Restoring CT_CC_CORE_STATIC_PREFIX_DIR='${CT_CC_CORE_STATIC_PREFIX_DIR}'"
   825     CT_Pushd "${CT_CC_CORE_STATIC_PREFIX_DIR}"
   826     CT_DoExecLog DEBUG tar xv${tar_opt}f "${state_dir}/cc_core_static_prefix_dir.tar${tar_ext}"
   827     CT_Popd
   828 
   829     CT_DoLog DEBUG "  Restoring CT_CONFIG_DIR='${CT_CONFIG_DIR}'"
   830     CT_Pushd "${CT_CONFIG_DIR}"
   831     CT_DoExecLog DEBUG tar xv${tar_opt}f "${state_dir}/config_dir.tar${tar_ext}"
   832     CT_Popd
   833 
   834     # Restore the environment, discarding any error message
   835     # (for example, read-only bash internals)
   836     CT_DoLog DEBUG "  Restoring environment"
   837     . "${state_dir}/env.sh" >/dev/null 2>&1 || true
   838 
   839     # Restore the new RESTART and STOP steps
   840     CT_RESTART="${old_RESTART}"
   841     CT_STOP="${old_STOP}"
   842     unset old_stop old_restart
   843 
   844     if [ "${CT_LOG_TO_FILE}" = "y" ]; then
   845         CT_DoLog DEBUG "  Restoring log file"
   846         exec >/dev/null
   847         case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
   848             y)  zcat "${state_dir}/log.gz" >"${CT_LOG_FILE}";;
   849             *)  cat "${state_dir}/log" >"${CT_LOG_FILE}";;
   850         esac
   851         cat "${state_dir}/tail.log" >>"${CT_LOG_FILE}"
   852         exec >>"${CT_LOG_FILE}"
   853         rm -f "${state_dir}/tail.log"
   854     fi
   855 }