scripts/functions
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Tue May 08 20:41:08 2007 +0000 (2007-05-08)
changeset 81 a20be197429a
parent 79 1b5fe9bbe694
child 82 a1c93f975268
permissions -rw-r--r--
Hop, a somewhat more efficient progress bar, less CPU consuming, if that was a problem :-)
(a litlle recreation while toochains are building...)
     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 CT_OnError() {
     6     ret=$?
     7     CT_DoLog ERROR "Build failed in step \"${CT_STEP_MESSAGE[${CT_STEP_COUNT}]}\""
     8     for((step=(CT_STEP_COUNT-1); step>1; step--)); do
     9         CT_DoLog ERROR "      called in step \"${CT_STEP_MESSAGE[${step}]}\""
    10     done
    11     CT_DoLog ERROR "Error happened in \"${BASH_SOURCE[1]}\" in function \"${FUNCNAME[1]}\" (line unknown, sorry)"
    12     for((depth=2; ${BASH_LINENO[$((${depth}-1))]}>0; depth++)); do
    13         CT_DoLog ERROR "      called from \"${BASH_SOURCE[${depth}]}\" at line # ${BASH_LINENO[${depth}-1]} in function \"${FUNCNAME[${depth}]}\""
    14     done
    15     CT_DoLog ERROR "Look at \"${CT_ACTUAL_LOG_FILE}\" for more info on this error."
    16     exit $ret
    17 }
    18 trap CT_OnError ERR
    19 
    20 set -E
    21 set -o pipefail
    22 
    23 # This is crosstool-ng-0.0.1
    24 CT_VERSION=ng-0.0.1
    25 
    26 # The different log levels:
    27 CT_LOG_LEVEL_ERROR=0
    28 CT_LOG_LEVEL_WARN=1
    29 CT_LOG_LEVEL_INFO=2
    30 CT_LOG_LEVEL_EXTRA=3
    31 CT_LOG_LEVEL_DEBUG=4
    32 CT_LOG_LEVEL_ALL=5
    33 
    34 # Attributes
    35 _A_NOR="\\033[0m"
    36 _A_BRI="\\033[1m"
    37 _A_DIM="\\033[2m"
    38 _A_UND="\\033[4m"
    39 _A_BRB="\\033[5m"
    40 _A_REV="\\033[7m"
    41 _A_HID="\\033[8m"
    42 
    43 # Fore colors
    44 _F_BLK="\\033[30m"
    45 _F_RED="\\033[31m"
    46 _F_GRN="\\033[32m"
    47 _F_YEL="\\033[33m"
    48 _F_BLU="\\033[34m"
    49 _F_MAG="\\033[35m"
    50 _F_CYA="\\033[36m"
    51 _F_WHI="\\033[37m"
    52 
    53 # A function to log what is happening
    54 # Different log level are available:
    55 #   - ERROR:   A serious, fatal error occurred
    56 #   - WARN:    A non fatal, non serious error occurred, take your responsbility with the generated build
    57 #   - INFO:    Informational messages
    58 #   - EXTRA:   Extra informational messages
    59 #   - DEBUG:   Debug messages
    60 #   - ALL:     Component's build messages
    61 # Usage: CT_DoLog <level> [message]
    62 # If message is empty, then stdin will be logged.
    63 CT_DoLog() {
    64     local max_level LEVEL level cur_l cur_L
    65     local l
    66     eval max_level="\${CT_LOG_LEVEL_${CT_LOG_LEVEL_MAX}}"
    67     # Set the maximum log level to DEBUG if we have none
    68     [ -z "${max_level}" ] && max_level=${CT_LOG_LEVEL_DEBUG}
    69 
    70     LEVEL="$1"; shift
    71     eval level="\${CT_LOG_LEVEL_${LEVEL}}"
    72 
    73     if [ $# -eq 0 ]; then
    74         cat -
    75     else
    76         echo "${1}"
    77     fi |( IFS="\n" # We want the full lines, even leading spaces
    78           cpt=0
    79           indent=$((2*CT_STEP_COUNT))
    80           while read line; do
    81               case "${CT_LOG_SEE_TOOLS_WARN},${line}" in
    82                 y,*"warning:"*)         cur_L=WARN; cur_l=${CT_LOG_LEVEL_WARN};;
    83                 *"error:"*)             cur_L=ERROR; cur_l=${CT_LOG_LEVEL_ERROR};;
    84                 *"make["?*"]:"*"Stop.") cur_L=ERROR; cur_l=${CT_LOG_LEVEL_ERROR};;
    85                 *)                      cur_L="${LEVEL}"; cur_l="${level}";;
    86               esac
    87               l="`printf \"[%-5s]%*s%s%s\" \"${cur_L}\" \"${indent}\" \" \" \"${line}\"`"
    88               # There will always be a log file, be it /dev/null
    89               echo -e "${l}" >>"${CT_ACTUAL_LOG_FILE}"
    90               color="CT_${cur_L}_COLOR"
    91               normal="CT_NORMAL_COLOR"
    92               if [ ${cur_l} -le ${max_level} ]; then
    93                   echo -e "\r${!color}${l}${!normal}"
    94                   CT_PROG_BAR_CPT=0 # Force redrawing progress bar on next 'unlogged' line
    95               else
    96                   ${CT_PROG_BAR}
    97               fi
    98           done
    99         )
   100 
   101     return 0
   102 }
   103 
   104 # Abort the execution with a error message
   105 # Usage: CT_Abort <message>
   106 CT_Abort() {
   107     CT_DoLog ERROR "$1" >&2
   108     exit 1
   109 }
   110 
   111 # Test a condition, and print a message if satisfied
   112 # Usage: CT_Test <message> <tests>
   113 CT_Test() {
   114     local ret
   115     local m="$1"
   116     shift
   117     test "$@" && CT_DoLog WARN "$m"
   118     return 0
   119 }
   120 
   121 # Test a condition, and abort with an error message if satisfied
   122 # Usage: CT_TestAndAbort <message> <tests>
   123 CT_TestAndAbort() {
   124     local m="$1"
   125     shift
   126     test "$@" && CT_Abort "$m"
   127     return 0
   128 }
   129 
   130 # Test a condition, and abort with an error message if not satisfied
   131 # Usage: CT_TestAndAbort <message> <tests>
   132 CT_TestOrAbort() {
   133     local m="$1"
   134     shift
   135     test "$@" || CT_Abort "$m"
   136     return 0
   137 }
   138 
   139 # Test the presence of a tool, or abort if not found
   140 # Usage: CT_HasOrAbort <tool>
   141 CT_HasOrAbort() {
   142     CT_TestAndAbort "\"${1}\" not found and needed for successfull toolchain build." -z "`which \"${1}\"`"
   143     return 0
   144 }
   145 
   146 # Get current date with nanosecond precision
   147 # On those system not supporting nanosecond precision, faked with rounding down
   148 # to the highest entire second
   149 # Usage: CT_DoDate <fmt>
   150 CT_DoDate() {
   151     date "$1" |sed -r -e 's/%N$/000000000/;'
   152 }
   153 
   154 CT_STEP_COUNT=1
   155 CT_STEP_MESSAGE[${CT_STEP_COUNT}]="<none>"
   156 # Memorise a step being done so that any error is caught
   157 # Usage: CT_DoStep <loglevel> <message>
   158 CT_DoStep() {
   159     local start=`CT_DoDate +%s%N`
   160     CT_DoLog "$1" "================================================================="
   161     CT_DoLog "$1" "$2"
   162     CT_STEP_COUNT=$((CT_STEP_COUNT+1))
   163     CT_STEP_LEVEL[${CT_STEP_COUNT}]="$1"; shift
   164     CT_STEP_START[${CT_STEP_COUNT}]="${start}"
   165     CT_STEP_MESSAGE[${CT_STEP_COUNT}]="$1"
   166     return 0
   167 }
   168 
   169 # End the step just being done
   170 # Usage: CT_EndStep
   171 CT_EndStep() {
   172     local stop=`CT_DoDate +%s%N`
   173     local duration=`printf "%032d" $((stop-${CT_STEP_START[${CT_STEP_COUNT}]})) |sed -r -e 's/([[:digit:]]{2})[[:digit:]]{7}$/\.\1/; s/^0+//; s/^\./0\./;'`
   174     local level="${CT_STEP_LEVEL[${CT_STEP_COUNT}]}"
   175     local message="${CT_STEP_MESSAGE[${CT_STEP_COUNT}]}"
   176     CT_STEP_COUNT=$((CT_STEP_COUNT-1))
   177     CT_DoLog "${level}" "${message}: done in ${duration}s"
   178     return 0
   179 }
   180 
   181 # Pushes into a directory, and pops back
   182 CT_Pushd() {
   183     pushd "$1" >/dev/null 2>&1
   184 }
   185 CT_Popd() {
   186     popd >/dev/null 2>&1
   187 }
   188 
   189 # Makes a path absolute
   190 # Usage: CT_MakeAbsolutePath path
   191 CT_MakeAbsolutePath() {
   192     # Try to cd in that directory
   193     if [ -d "$1" ]; then
   194         CT_Pushd "$1"
   195         pwd
   196         CT_Popd
   197     else
   198         # No such directory, fail back to guessing
   199         case "$1" in
   200             /*)  echo "$1";;
   201             *)   echo "`pwd`/$1";;
   202         esac
   203     fi
   204     
   205     return 0
   206 }
   207 
   208 # Creates a temporary directory
   209 # $1: variable to assign to
   210 # Usage: CT_MktempDir foo
   211 CT_MktempDir() {
   212     # Some mktemp do not allow more than 6 Xs
   213     eval "$1"="`mktemp -q -d \"${CT_BUILD_DIR}/.XXXXXX\"`"
   214     CT_TestOrAbort "Could not make temporary directory" -n "${!1}" -a -d "${!1}"
   215 }
   216 
   217 # Echoes the specified string on stdout until the pipe breaks.
   218 # Doesn't fail
   219 # $1: string to echo
   220 # Usage: CT_DoYes "" |make oldconfig
   221 CT_DoYes() {
   222     yes "$1" || true
   223 }
   224 
   225 # Download an URL using wget
   226 # Usage: CT_DoGetFileWget <URL>
   227 CT_DoGetFileWget() {
   228     # Need to return true because it is legitimate to not find the tarball at
   229     # some of the provided URLs (think about snapshots, different layouts for
   230     # different gcc versions, etc...)
   231     # Some (very old!) FTP server might not support the passive mode, thus
   232     # retry without
   233     # With automated download as we are doing, it can be very dangerous to use
   234     # -c to continue the downloads. It's far better to simply overwrite the
   235     # destination file
   236     wget -nc --progress=dot:binary --tries=3 --passive-ftp "$1" || wget -nc --progress=dot:binary --tries=3 "$1" || true
   237 }
   238 
   239 # Download an URL using curl
   240 # Usage: CT_DoGetFileCurl <URL>
   241 CT_DoGetFileCurl() {
   242 	# Note: comments about wget method are also valid here
   243 	# Plus: no good progreess indicator is available with curl,
   244 	#       so output is consigned to oblivion
   245 	curl --ftp-pasv -O --retry 3 "$1" >/dev/null || curl -O --retry 3 "$1" >/dev/null || true
   246 }
   247 
   248 # Wrapper function to call one of curl or wget
   249 # Usage: CT_DoGetFile <URL>
   250 CT_DoGetFile() {
   251     local _wget=`which wget`
   252     local _curl=`which curl`
   253     case "${_wget},${_curl}" in
   254         ,)  CT_DoError "Could find neither wget nor curl";;
   255         ,*) CT_DoGetFileCurl "$1" 2>&1 |CT_DoLog DEBUG;;
   256         *)  CT_DoGetFileWget "$1" 2>&1 |CT_DoLog DEBUG;;
   257     esac
   258 }
   259 
   260 # Download the file from one of the URLs passed as argument
   261 # Usage: CT_GetFile <filename> <url> [<url> ...]
   262 CT_GetFile() {
   263     local got_it
   264     local ext
   265     local url
   266     local file="$1"
   267     shift
   268 
   269     # Do we already have it?
   270     ext=`CT_GetFileExtension "${file}"`
   271     if [ -n "${ext}" ]; then
   272         if [ "${CT_FORCE_DOWNLOAD}" = "y" ]; then
   273             CT_DoLog DEBUG "Removing already present \"${file}\""
   274             rm -f "${CT_TARBALLS_DIR}/${file}${ext}"
   275         else
   276             CT_DoLog DEBUG "Already have \"${file}\""
   277             return 0
   278         fi
   279     fi
   280 
   281     CT_DoLog EXTRA "Retrieving \"${file}\""
   282     CT_Pushd "${CT_TARBALLS_DIR}"
   283     # File not yet downloaded, try to get it
   284     got_it=0
   285     if [ "${got_it}" != "y" ]; then
   286         # We'd rather have a bzip2'ed tarball, then gzipped, and finally plain tar.
   287         for ext in .tar.bz2 .tar.gz .tgz .tar; do
   288             # Try all urls in turn
   289             for url in "$@"; do
   290                 case "${url}" in
   291                     *)  CT_DoLog DEBUG "Trying \"${url}/${file}${ext}\""
   292                         CT_DoGetFile "${url}/${file}${ext}"
   293                         ;;
   294                 esac
   295                 [ -f "${file}${ext}" ] && got_it=1 && break 2 || true
   296             done
   297         done
   298     fi
   299     CT_Popd
   300 
   301     CT_TestAndAbort "Could not download \"${file}\", and not present in \"${CT_TARBALLS_DIR}\"" ${got_it} -eq 0
   302 }
   303 
   304 # Get the file name extension of a component
   305 # Usage: CT_GetFileExtension <component_name-component_version>
   306 # If found, echoes the extension to stdout
   307 # If not found, echoes nothing on stdout.
   308 CT_GetFileExtension() {
   309     local ext
   310     local file="$1"
   311     local got_it=1
   312 
   313     CT_Pushd "${CT_TARBALLS_DIR}"
   314     for ext in .tar.gz .tar.bz2 .tgz .tar; do
   315         if [ -f "${file}${ext}" ]; then
   316             echo "${ext}"
   317             got_it=0
   318             break
   319         fi
   320     done
   321     CT_Popd
   322 
   323     return 0
   324 }
   325 
   326 # Extract a tarball and patch the resulting sources if necessary.
   327 # Some tarballs need to be extracted in specific places. Eg.: glibc addons
   328 # must be extracted in the glibc directory; uCLibc locales must be extracted
   329 # in the extra/locale sub-directory of uClibc.
   330 CT_ExtractAndPatch() {
   331     local file="$1"
   332     local base_file=`echo "${file}" |cut -d - -f 1`
   333     local ver_file=`echo "${file}" |cut -d - -f 2-`
   334     local official_patch_dir
   335     local custom_patch_dir
   336     local libc_addon
   337     local ext=`CT_GetFileExtension "${file}"`
   338     CT_TestAndAbort "\"${file}\" not found in \"${CT_TARBALLS_DIR}\"" -z "${ext}"
   339     local full_file="${CT_TARBALLS_DIR}/${file}${ext}"
   340 
   341     CT_Pushd "${CT_SRC_DIR}"
   342 
   343     # Add-ons need a little love, really.
   344     case "${file}" in
   345         glibc-[a-z]*-*)
   346             CT_TestAndAbort "Trying to extract the C-library addon/locales \"${file}\" when C-library not yet extracted" ! -d "${CT_LIBC_FILE}"
   347             cd "${CT_LIBC_FILE}"
   348             libc_addon=y
   349             [ -f ".${file}.extracted" ] && return 0
   350             touch ".${file}.extracted"
   351             ;;
   352         uClibc-locale-*)
   353             CT_TestAndAbort "Trying to extract the C-library addon/locales \"${file}\" when C-library not yet extracted" ! -d "${CT_LIBC_FILE}"
   354             cd "${CT_LIBC_FILE}/extra/locale"
   355             libc_addon=y
   356             [ -f ".${file}.extracted" ] && return 0
   357             touch ".${file}.extracted"
   358             ;;
   359     esac
   360 
   361     # If the directory exists, then consider extraction and patching done
   362     if [ -d "${file}" ]; then
   363         CT_DoLog DEBUG "Already extracted \"${file}\""
   364         return 0
   365     fi
   366 
   367     CT_DoLog EXTRA "Extracting \"${file}\""
   368     case "${ext}" in
   369         .tar.bz2)     tar xvjf "${full_file}" |CT_DoLog ALL;;
   370         .tar.gz|.tgz) tar xvzf "${full_file}" |CT_DoLog ALL;;
   371         .tar)         tar xvf  "${full_file}" |CT_DoLog ALL;;
   372         *)            CT_Abort "Don't know how to handle \"${file}\": unknown extension" ;;
   373     esac
   374 
   375     # Snapshots might not have the version number in the extracted directory
   376     # name. This is also the case for some (old) packages, such as libfloat.
   377     # Overcome this issue by symlink'ing the directory.
   378     if [ ! -d "${file}" -a "${libc_addon}" != "y" ]; then
   379         case "${ext}" in
   380             .tar.bz2)     base=`tar tjf "${full_file}" |head -n 1 |cut -d / -f 1 || true`;;
   381             .tar.gz|.tgz) base=`tar tzf "${full_file}" |head -n 1 |cut -d / -f 1 || true`;;
   382             .tar)         base=`tar tf  "${full_file}" |head -n 1 |cut -d / -f 1 || true`;;
   383         esac
   384         CT_TestOrAbort "There was a problem when extracting \"${file}\"" -d "${base}" -o "${base}" != "${file}"
   385         ln -s "${base}" "${file}"
   386     fi
   387 
   388     # Kludge: outside this function, we wouldn't know if we had just extracted
   389     # a libc addon, or a plain package. Apply patches now.
   390     CT_DoLog EXTRA "Patching \"${file}\""
   391 
   392     # If libc addon, we're already in the correct place.
   393     [ -z "${libc_addon}" ] && cd "${file}"
   394 
   395     [ "${CUSTOM_PATCH_ONLY}" = "y" ] || official_patch_dir="${CT_TOP_DIR}/patches/${base_file}/${ver_file}"
   396     [ "${CT_CUSTOM_PATCH}" = "y" ] && custom_patch_dir="${CT_CUSTOM_PATCH_DIR}/${base_file}/${ver_file}"
   397     for patch_dir in "${official_patch_dir}" "${custom_patch_dir}"; do
   398         if [ -n "${patch_dir}" -a -d "${patch_dir}" ]; then
   399             for p in "${patch_dir}"/*.patch; do
   400                 if [ -f "${p}" ]; then
   401                     CT_DoLog DEBUG "Applying patch \"${p}\""
   402                     patch -g0 -F1 -p1 -f <"${p}" |CT_DoLog ALL
   403                     CT_TestAndAbort "Failed while applying patch file \"${p}\"" ${PIPESTATUS[0]} -ne 0
   404                 fi
   405             done
   406         fi
   407     done
   408 
   409     CT_Popd
   410 }
   411 
   412 # Compute the target triplet from what is provided by the user
   413 # Usage: CT_DoBuildTargetTriplet
   414 # In fact this function takes the environment variables to build the target
   415 # triplet. It is needed both by the normal build sequence, as well as the
   416 # sample saving sequence.
   417 CT_DoBuildTargetTriplet() {
   418     case "${CT_ARCH_BE},${CT_ARCH_LE}" in
   419         y,) target_endian_eb=eb; target_endian_el=;;
   420         ,y) target_endian_eb=; target_endian_el=el;;
   421     esac
   422     case "${CT_ARCH}" in
   423         arm)  CT_TARGET="${CT_ARCH}${target_endian_eb}";;
   424         mips) CT_TARGET="${CT_ARCH}${target_endian_el}";;
   425         x86*) # Much love for this one :-(
   426               # Ultimately, we should use config.sub to output the correct
   427               # procesor name. Work for later...
   428               arch="${CT_ARCH_ARCH}"
   429               [ -z "${arch}" ] && arch="${CT_ARCH_TUNE}"
   430               case "${CT_ARCH}" in
   431                   x86_64)      CT_TARGET=x86_64;;
   432               	  *)  case "${arch}" in
   433                           "")                                       CT_TARGET=i386;;
   434                           i386|i486|i586|i686)                      CT_TARGET="${arch}";;
   435                           winchip*)                                 CT_TARGET=i486;;
   436                           pentium|pentium-mmx|c3*)                  CT_TARGET=i586;;
   437                           nocona|athlon*64|k8|athlon-fx|opteron)    CT_TARGET=x86_64;;
   438                           pentiumpro|pentium*|athlon*)              CT_TARGET=i686;;
   439                           *)                                        CT_TARGET=i586;;
   440                       esac;;
   441               esac;;
   442     esac
   443     case "${CT_TARGET_VENDOR}" in
   444         "") CT_TARGET="${CT_TARGET}-unknown";;
   445         *)  CT_TARGET="${CT_TARGET}-${CT_TARGET_VENDOR}";;
   446     esac
   447     case "${CT_KERNEL}" in
   448         linux*)  CT_TARGET="${CT_TARGET}-linux";;
   449         cygwin*) CT_TARGET="${CT_TARGET}-cygwin";;
   450     esac
   451     case "${CT_LIBC}" in
   452         glibc)  CT_TARGET="${CT_TARGET}-gnu";;
   453         uClibc) CT_TARGET="${CT_TARGET}-uclibc";;
   454     esac
   455     case "${CT_ARCH_ABI}" in
   456         eabi)   CT_TARGET="${CT_TARGET}eabi";;
   457     esac
   458     CT_TARGET="`${CT_TOP_DIR}/tools/config.sub ${CT_TARGET}`"
   459 }