scripts/functions
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Sun Mar 20 00:02:21 2011 +0100 (2011-03-20)
changeset 2339 730e2d63296b
parent 2331 bbfd61c12916
child 2340 580f427595bc
permissions -rw-r--r--
scripts: leave changelog in build dir, copy to install dir

Users tend to look for the build log in the current working directory,
rather than in the toolchain's installation dir. While bundling the build
log in the toolchain installation dir is nice for distribution and review,
it can be easier to have the build log readily available in the working
directory, as it is quicker to get to it.

So, the build log stays in the working directory until the toolchain is
completely and successfully built, and then a (compressed) copy is made.

Reported-by: Trevor Woerner <twoerner@gmail.com>
Signed-off-by: "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
anthony@2155
     1
# This file contains some usefull common functions -*- sh -*-
yann@1
     2
# Copyright 2007 Yann E. MORIN
yann@1
     3
# Licensed under the GPL v2. See COPYING in the root of this package
yann@1
     4
yann@136
     5
# Prepare the fault handler
yann@1
     6
CT_OnError() {
yann@1
     7
    ret=$?
yann@726
     8
    # Bail out early in subshell, the upper level shell will act accordingly.
yann@726
     9
    [ ${BASH_SUBSHELL} -eq 0 ] || exit $ret
yann@523
    10
    CT_DoLog ERROR "Build failed in step '${CT_STEP_MESSAGE[${CT_STEP_COUNT}]}'"
yann@1
    11
    for((step=(CT_STEP_COUNT-1); step>1; step--)); do
yann@523
    12
        CT_DoLog ERROR "      called in step '${CT_STEP_MESSAGE[${step}]}'"
yann@1
    13
    done
yann@523
    14
    CT_DoLog ERROR "Error happened in '${BASH_SOURCE[1]}' in function '${FUNCNAME[1]}' (line unknown, sorry)"
yann@1
    15
    for((depth=2; ${BASH_LINENO[$((${depth}-1))]}>0; depth++)); do
yann@523
    16
        CT_DoLog ERROR "      called from '${BASH_SOURCE[${depth}]}' at line # ${BASH_LINENO[${depth}-1]} in function '${FUNCNAME[${depth}]}'"
yann@1
    17
    done
yann@2339
    18
    [ "${CT_LOG_TO_FILE}" = "y" ] && CT_DoLog ERROR "Look at '${tmp_log_file}' for more info on this error."
yann@96
    19
    CT_STEP_COUNT=1
yann@96
    20
    CT_DoEnd ERROR
yann@1
    21
    exit $ret
yann@1
    22
}
yann@136
    23
yann@136
    24
# Install the fault handler
yann@1
    25
trap CT_OnError ERR
yann@1
    26
yann@136
    27
# Inherit the fault handler in subshells and functions
yann@1
    28
set -E
yann@136
    29
yann@136
    30
# Make pipes fail on the _first_ failed command
yann@136
    31
# Not supported on bash < 3.x, but we need it, so drop the obsoleting bash-2.x
yann@1
    32
set -o pipefail
yann@1
    33
yann@136
    34
# Don't hash commands' locations, and search every time it is requested.
yann@136
    35
# This is slow, but needed because of the static/shared core gcc which shall
yann@136
    36
# always match to shared if it exists, and only fallback to static if the
yann@136
    37
# shared is not found
yann@136
    38
set +o hashall
yann@136
    39
yann@165
    40
# Log policy:
yann@165
    41
#  - first of all, save stdout so we can see the live logs: fd #6
yann@165
    42
exec 6>&1
yann@2339
    43
#  - then point stdout to the log file
yann@2339
    44
tmp_log_file="${CT_TOP_DIR}/build.log"
yann@2339
    45
rm -f "${tmp_log_file}"
yann@165
    46
exec >>"${tmp_log_file}"
yann@165
    47
yann@1
    48
# The different log levels:
yann@1
    49
CT_LOG_LEVEL_ERROR=0
yann@1
    50
CT_LOG_LEVEL_WARN=1
yann@1
    51
CT_LOG_LEVEL_INFO=2
yann@1
    52
CT_LOG_LEVEL_EXTRA=3
anthony@2154
    53
CT_LOG_LEVEL_CFG=4
anthony@2154
    54
CT_LOG_LEVEL_FILE=5
anthony@2155
    55
CT_LOG_LEVEL_STATE=6
anthony@2155
    56
CT_LOG_LEVEL_ALL=7
anthony@2155
    57
CT_LOG_LEVEL_DEBUG=8
yann@1
    58
yann@1083
    59
# Make it easy to use \n and !
yann@1081
    60
CR=$(printf "\n")
yann@1083
    61
BANG='!'
yann@1081
    62
yann@1
    63
# A function to log what is happening
yann@1
    64
# Different log level are available:
yann@1
    65
#   - ERROR:   A serious, fatal error occurred
yann@1
    66
#   - WARN:    A non fatal, non serious error occurred, take your responsbility with the generated build
yann@1
    67
#   - INFO:    Informational messages
yann@1
    68
#   - EXTRA:   Extra informational messages
anthony@2154
    69
#   - CFG:     Output of various "./configure"-type scripts
anthony@2154
    70
#   - FILE:    File / archive unpacking.
anthony@2155
    71
#   - STATE:   State save & restore
yann@78
    72
#   - ALL:     Component's build messages
anthony@2155
    73
#   - DEBUG:   Internal debug messages
yann@1
    74
# Usage: CT_DoLog <level> [message]
yann@1
    75
# If message is empty, then stdin will be logged.
yann@1
    76
CT_DoLog() {
yann@47
    77
    local max_level LEVEL level cur_l cur_L
yann@47
    78
    local l
yann@1
    79
    eval max_level="\${CT_LOG_LEVEL_${CT_LOG_LEVEL_MAX}}"
yann@1
    80
    # Set the maximum log level to DEBUG if we have none
yann@78
    81
    [ -z "${max_level}" ] && max_level=${CT_LOG_LEVEL_DEBUG}
yann@1
    82
yann@47
    83
    LEVEL="$1"; shift
yann@1
    84
    eval level="\${CT_LOG_LEVEL_${LEVEL}}"
yann@1
    85
yann@1
    86
    if [ $# -eq 0 ]; then
yann@1
    87
        cat -
yann@1
    88
    else
yann@2096
    89
        printf "%s\n" "${*}"
yann@1081
    90
    fi |( IFS="${CR}" # We want the full lines, even leading spaces
yann@523
    91
          _prog_bar_cpt=0
yann@523
    92
          _prog_bar[0]='/'
yann@523
    93
          _prog_bar[1]='-'
yann@523
    94
          _prog_bar[2]='\'
yann@523
    95
          _prog_bar[3]='|'
yann@1
    96
          indent=$((2*CT_STEP_COUNT))
yann@1
    97
          while read line; do
yann@47
    98
              case "${CT_LOG_SEE_TOOLS_WARN},${line}" in
yann@47
    99
                y,*"warning:"*)         cur_L=WARN; cur_l=${CT_LOG_LEVEL_WARN};;
yann@112
   100
                y,*"WARNING:"*)         cur_L=WARN; cur_l=${CT_LOG_LEVEL_WARN};;
yann@47
   101
                *"error:"*)             cur_L=ERROR; cur_l=${CT_LOG_LEVEL_ERROR};;
yann@919
   102
                *"make["*"]: *** ["*)   cur_L=ERROR; cur_l=${CT_LOG_LEVEL_ERROR};;
yann@47
   103
                *)                      cur_L="${LEVEL}"; cur_l="${level}";;
yann@47
   104
              esac
yann@523
   105
              # There will always be a log file (stdout, fd #1), be it /dev/null
yann@523
   106
              printf "[%-5s]%*s%s%s\n" "${cur_L}" "${indent}" " " "${line}"
yann@47
   107
              if [ ${cur_l} -le ${max_level} ]; then
yann@523
   108
                  # Only print to console (fd #6) if log level is high enough.
yann@523
   109
                  printf "\r[%-5s]%*s%s%s\n" "${cur_L}" "${indent}" " " "${line}" >&6
yann@82
   110
              fi
yann@82
   111
              if [ "${CT_LOG_PROGRESS_BAR}" = "y" ]; then
yann@523
   112
                  printf "\r[%02d:%02d] %s " $((SECONDS/60)) $((SECONDS%60)) "${_prog_bar[$((_prog_bar_cpt/10))]}" >&6
yann@523
   113
                  _prog_bar_cpt=$(((_prog_bar_cpt+1)%40))
yann@1
   114
              fi
yann@1
   115
          done
yann@1
   116
        )
yann@1
   117
yann@1
   118
    return 0
yann@1
   119
}
yann@1
   120
yann@535
   121
# Execute an action, and log its messages
yann@1257
   122
# Usage: [VAR=val...] CT_DoExecLog <level> <command [parameters...]>
yann@535
   123
CT_DoExecLog() {
yann@535
   124
    local level="$1"
yann@535
   125
    shift
yann@1516
   126
    CT_DoLog DEBUG "==> Executing: '${*}'"
yann@668
   127
    "${@}" 2>&1 |CT_DoLog "${level}"
yann@535
   128
}
yann@535
   129
yann@96
   130
# Tail message to be logged whatever happens
yann@96
   131
# Usage: CT_DoEnd <level>
yann@96
   132
CT_DoEnd()
yann@96
   133
{
yann@145
   134
    local level="$1"
yann@523
   135
    CT_STOP_DATE=$(CT_DoDate +%s%N)
yann@523
   136
    CT_STOP_DATE_HUMAN=$(CT_DoDate +%Y%m%d.%H%M%S)
yann@599
   137
    if [ "${level}" != "ERROR" ]; then
yann@582
   138
        CT_DoLog "${level:-INFO}" "Build completed at ${CT_STOP_DATE_HUMAN}"
yann@582
   139
    fi
yann@96
   140
    elapsed=$((CT_STOP_DATE-CT_STAR_DATE))
yann@96
   141
    elapsed_min=$((elapsed/(60*1000*1000*1000)))
yann@523
   142
    elapsed_sec=$(printf "%02d" $(((elapsed%(60*1000*1000*1000))/(1000*1000*1000))))
yann@523
   143
    elapsed_csec=$(printf "%02d" $(((elapsed%(1000*1000*1000))/(10*1000*1000))))
yann@145
   144
    CT_DoLog ${level:-INFO} "(elapsed: ${elapsed_min}:${elapsed_sec}.${elapsed_csec})"
yann@96
   145
}
yann@96
   146
yann@2051
   147
# Remove entries referring to . and other relative paths
js@2044
   148
# Usage: CT_SanitizePath
js@2044
   149
CT_SanitizePath() {
js@2044
   150
    local new
yann@2051
   151
    local p
js@2044
   152
    local IFS=:
js@2044
   153
    for p in $PATH; do
yann@2051
   154
        # Only accept absolute paths;
yann@2051
   155
        # Note: as a special case the empty string in PATH is equivalent to .
yann@2051
   156
        if [ -n "${p}" -a -z "${p%%/*}" ]; then
js@2044
   157
            new="${new}${new:+:}${p}"
js@2044
   158
        fi
js@2044
   159
    done
js@2044
   160
    PATH="${new}"
js@2044
   161
}
js@2044
   162
yann@2280
   163
# Sanitise the directory name contained in the variable passed as argument:
yann@2280
   164
# - remove duplicate /
yann@2280
   165
# Usage: CT_SanitiseVarDir CT_PREFIX_DIR
yann@2280
   166
CT_SanitiseVarDir() {
yann@2280
   167
    local var
yann@2280
   168
    local old_dir
yann@2280
   169
    local new_dir
yann@2280
   170
yann@2280
   171
    for var in "$@"; do
yann@2280
   172
        eval "old_dir=\"\${${var}}\""
yann@2280
   173
        new_dir="$( printf "${old_dir}"     \
yann@2280
   174
                    |sed -r -e 's:/+:/:g;'  \
yann@2280
   175
                  )"
yann@2280
   176
        eval "${var}=\"${new_dir}\""
yann@2280
   177
        CT_DoLog DEBUG "Sanitised '${var}': '${old_dir}' -> '${new_dir}'"
yann@2280
   178
    done
yann@2280
   179
}
yann@2280
   180
yann@96
   181
# Abort the execution with an error message
yann@1
   182
# Usage: CT_Abort <message>
yann@1
   183
CT_Abort() {
yann@128
   184
    CT_DoLog ERROR "$1"
yann@1
   185
    exit 1
yann@1
   186
}
yann@1
   187
yann@1
   188
# Test a condition, and print a message if satisfied
yann@1
   189
# Usage: CT_Test <message> <tests>
yann@1
   190
CT_Test() {
yann@1
   191
    local ret
yann@1
   192
    local m="$1"
yann@1
   193
    shift
yann@1909
   194
    CT_DoLog DEBUG "Testing '! ( $* )'"
yann@1
   195
    test "$@" && CT_DoLog WARN "$m"
yann@1
   196
    return 0
yann@1
   197
}
yann@1
   198
yann@1
   199
# Test a condition, and abort with an error message if satisfied
yann@1
   200
# Usage: CT_TestAndAbort <message> <tests>
yann@1
   201
CT_TestAndAbort() {
yann@1
   202
    local m="$1"
yann@1
   203
    shift
yann@1909
   204
    CT_DoLog DEBUG "Testing '! ( $* )'"
yann@1
   205
    test "$@" && CT_Abort "$m"
yann@1
   206
    return 0
yann@1
   207
}
yann@1
   208
yann@1
   209
# Test a condition, and abort with an error message if not satisfied
yann@1
   210
# Usage: CT_TestAndAbort <message> <tests>
yann@1
   211
CT_TestOrAbort() {
yann@1
   212
    local m="$1"
yann@1
   213
    shift
yann@1909
   214
    CT_DoLog DEBUG "Testing '$*'"
yann@1
   215
    test "$@" || CT_Abort "$m"
yann@1
   216
    return 0
yann@1
   217
}
yann@1
   218
yann@1
   219
# Test the presence of a tool, or abort if not found
yann@1
   220
# Usage: CT_HasOrAbort <tool>
yann@1
   221
CT_HasOrAbort() {
yann@773
   222
    CT_TestAndAbort "'${1}' not found and needed for successful toolchain build." -z "$(CT_Which "${1}")"
yann@1
   223
    return 0
yann@1
   224
}
yann@1
   225
yann@210
   226
# Search a program: wrap "which" for those system where
yann@1226
   227
# "which" verbosely says there is no match (Mandriva is
yann@1226
   228
# such a sucker...)
yann@210
   229
# Usage: CT_Which <filename>
yann@210
   230
CT_Which() {
yann@210
   231
  which "$1" 2>/dev/null || true
yann@210
   232
}
yann@210
   233
yann@1
   234
# Get current date with nanosecond precision
yann@1
   235
# On those system not supporting nanosecond precision, faked with rounding down
yann@1
   236
# to the highest entire second
yann@1
   237
# Usage: CT_DoDate <fmt>
yann@1
   238
CT_DoDate() {
tvb377@1798
   239
    date "$1" |sed -r -e 's/N$/000000000/;'
yann@1
   240
}
yann@1
   241
yann@1
   242
CT_STEP_COUNT=1
yann@1
   243
CT_STEP_MESSAGE[${CT_STEP_COUNT}]="<none>"
yann@1
   244
# Memorise a step being done so that any error is caught
yann@1
   245
# Usage: CT_DoStep <loglevel> <message>
yann@1
   246
CT_DoStep() {
yann@523
   247
    local start=$(CT_DoDate +%s%N)
yann@1
   248
    CT_DoLog "$1" "================================================================="
yann@1
   249
    CT_DoLog "$1" "$2"
yann@1
   250
    CT_STEP_COUNT=$((CT_STEP_COUNT+1))
yann@1
   251
    CT_STEP_LEVEL[${CT_STEP_COUNT}]="$1"; shift
yann@1
   252
    CT_STEP_START[${CT_STEP_COUNT}]="${start}"
yann@1
   253
    CT_STEP_MESSAGE[${CT_STEP_COUNT}]="$1"
yann@1
   254
    return 0
yann@1
   255
}
yann@1
   256
yann@1
   257
# End the step just being done
yann@1
   258
# Usage: CT_EndStep
yann@1
   259
CT_EndStep() {
yann@523
   260
    local stop=$(CT_DoDate +%s%N)
yann@523
   261
    local duration=$(printf "%032d" $((stop-${CT_STEP_START[${CT_STEP_COUNT}]})) |sed -r -e 's/([[:digit:]]{2})[[:digit:]]{7}$/\.\1/; s/^0+//; s/^\./0\./;')
yann@582
   262
    local elapsed=$(printf "%02d:%02d" $((SECONDS/60)) $((SECONDS%60)))
yann@1
   263
    local level="${CT_STEP_LEVEL[${CT_STEP_COUNT}]}"
yann@1
   264
    local message="${CT_STEP_MESSAGE[${CT_STEP_COUNT}]}"
yann@1
   265
    CT_STEP_COUNT=$((CT_STEP_COUNT-1))
yann@582
   266
    CT_DoLog "${level}" "${message}: done in ${duration}s (at ${elapsed})"
yann@1
   267
    return 0
yann@1
   268
}
yann@1
   269
yann@1
   270
# Pushes into a directory, and pops back
yann@1
   271
CT_Pushd() {
yann@1
   272
    pushd "$1" >/dev/null 2>&1
yann@1
   273
}
yann@1
   274
CT_Popd() {
yann@1
   275
    popd >/dev/null 2>&1
yann@1
   276
}
yann@1
   277
yann@1
   278
# Creates a temporary directory
yann@1
   279
# $1: variable to assign to
yann@1
   280
# Usage: CT_MktempDir foo
yann@1
   281
CT_MktempDir() {
yann@1
   282
    # Some mktemp do not allow more than 6 Xs
yann@1113
   283
    eval "$1"=$(mktemp -q -d "${CT_BUILD_DIR}/tmp.XXXXXX")
yann@1
   284
    CT_TestOrAbort "Could not make temporary directory" -n "${!1}" -a -d "${!1}"
yann@787
   285
    CT_DoLog DEBUG "Made temporary directory '${!1}'"
yann@787
   286
    return 0
yann@1
   287
}
yann@1
   288
yann@1148
   289
# Removes one or more directories, even if it is read-only, or its parent is
yann@1138
   290
# Usage: CT_DoForceRmdir dir [...]
yann@1138
   291
CT_DoForceRmdir() {
yann@1148
   292
    local dir
yann@1148
   293
    local mode
yann@1148
   294
    for dir in "${@}"; do
yann@1148
   295
        [ -d "${dir}" ] || continue
titus@1956
   296
        case "$CT_SYS_OS" in
yann@2029
   297
            Linux|CYGWIN*)
titus@1956
   298
                mode="$(stat -c '%a' "$(dirname "${dir}")")"
titus@1956
   299
                ;;
titus@1956
   300
            Darwin|*BSD)
titus@1956
   301
                mode="$(stat -f '%Lp' "$(dirname "${dir}")")"
titus@1956
   302
                ;;
titus@1956
   303
            *)
titus@1956
   304
                CT_Abort "Unhandled host OS $CT_SYS_OS"
titus@1956
   305
                ;;
titus@1956
   306
        esac
yann@1185
   307
        CT_DoExecLog ALL chmod u+w "$(dirname "${dir}")"
yann@1185
   308
        CT_DoExecLog ALL chmod -R u+w "${dir}"
yann@1148
   309
        CT_DoExecLog ALL rm -rf "${dir}"
yann@1185
   310
        CT_DoExecLog ALL chmod ${mode} "$(dirname "${dir}")"
yann@1148
   311
    done
yann@1138
   312
}
yann@1138
   313
yann@1
   314
# Echoes the specified string on stdout until the pipe breaks.
yann@1
   315
# Doesn't fail
yann@1
   316
# $1: string to echo
yann@1
   317
# Usage: CT_DoYes "" |make oldconfig
yann@1
   318
CT_DoYes() {
yann@1
   319
    yes "$1" || true
yann@1
   320
}
yann@63
   321
yann@1391
   322
# Add the specified directory to LD_LIBRARY_PATH, and export it
yann@1391
   323
# If the specified patch is already present, just export
yann@1391
   324
# $1: path to add
yann@1391
   325
# $2: add as 'first' or 'last' path, 'first' is assumed if $2 is empty
yann@1391
   326
# Usage CT_SetLibPath /some/where/lib [first|last]
yann@1391
   327
CT_SetLibPath() {
yann@1391
   328
    local path="$1"
yann@1391
   329
    local pos="$2"
yann@1391
   330
yann@1391
   331
    case ":${LD_LIBRARY_PATH}:" in
yann@1391
   332
        *:"${path}":*)  ;;
yann@1391
   333
        *)  case "${pos}" in
yann@1391
   334
                last)
yann@1391
   335
                    CT_DoLog DEBUG "Adding '${path}' at end of LD_LIBRARY_PATH"
yann@1391
   336
                    LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}${path}"
yann@1391
   337
                    ;;
yann@1391
   338
                first|"")
yann@1391
   339
                    CT_DoLog DEBUG "Adding '${path}' at start of LD_LIBRARY_PATH"
yann@1391
   340
                    LD_LIBRARY_PATH="${path}${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
yann@1391
   341
                    ;;
yann@1391
   342
                *)
yann@1391
   343
                    CT_Abort "Incorrect position '${pos}' to add '${path}' to LD_LIBRARY_PATH"
yann@1391
   344
                    ;;
yann@1391
   345
            esac
yann@1391
   346
            ;;
yann@1391
   347
    esac
yann@1391
   348
    CT_DoLog DEBUG "==> LD_LIBRARY_PATH='${LD_LIBRARY_PATH}'"
yann@1391
   349
    export LD_LIBRARY_PATH
yann@1391
   350
}
yann@1391
   351
yann@85
   352
# Get the file name extension of a component
yann@719
   353
# Usage: CT_GetFileExtension <component_name-component_version> [extension]
yann@1690
   354
# If found, echoes the extension to stdout, and return 0
yann@1690
   355
# If not found, echoes nothing on stdout, and return !0.
yann@85
   356
CT_GetFileExtension() {
yann@85
   357
    local ext
yann@85
   358
    local file="$1"
yann@719
   359
    shift
yann@719
   360
    local first_ext="$1"
yann@85
   361
yann@160
   362
    # we need to also check for an empty extension for those very
yann@160
   363
    # peculiar components that don't have one (such as sstrip from
yann@160
   364
    # buildroot).
yann@1763
   365
    for ext in ${first_ext} .tar.gz .tar.bz2 .tgz .tar /.git ''; do
yann@1763
   366
        if [ -e "${CT_TARBALLS_DIR}/${file}${ext}" ]; then
yann@85
   367
            echo "${ext}"
yann@1690
   368
            exit 0
yann@85
   369
        fi
yann@85
   370
    done
yann@85
   371
yann@1690
   372
    exit 1
yann@85
   373
}
yann@85
   374
yann@2204
   375
# Try to retrieve the specified URL (HTTP or FTP)
yann@2204
   376
# Usage: CT_DoGetFile <URL>
yann@2204
   377
# This functions always returns true (0), as it can be legitimate not
yann@2204
   378
# to find the requested URL (think about snapshots, different layouts
yann@2204
   379
# for different gcc versions, etc...).
yann@2204
   380
CT_DoGetFile() {
yann@2205
   381
    local dest="${1##*/}"
yann@2205
   382
    local tmp="${dest}.tmp-dl"
yann@2204
   383
    # OK, just look if we have them...
yann@2204
   384
    # We are sure at least one is available, ./configure checked for it.
yann@2204
   385
    local _curl=$(CT_Which curl)
yann@2204
   386
    local _wget=$(CT_Which wget)
yann@2204
   387
    _curl="${_curl:-false}"
yann@2204
   388
    _wget="${_wget:-false}"
yann@2204
   389
yann@2205
   390
    # Remove potential left-over from a previous run
yann@2205
   391
    rm -f "${tmp}"
yann@2205
   392
yann@63
   393
    # Some (very old!) FTP server might not support the passive mode, thus
yann@2204
   394
    # retry without.
yann@2204
   395
    # We also retry a few times, in case there is a transient error (eg. behind
yann@2204
   396
    # a dynamic IP that changes during the transfer...)
yann@2204
   397
    # With automated download as we are doing, it can be very dangerous to
yann@2204
   398
    # continue the downloads. It's far better to simply overwrite the
yann@2204
   399
    # destination file.
yann@492
   400
    # Some company networks have firewalls to connect to the internet, but it's
yann@1113
   401
    # not easy to detect them, and wget does not timeout by default while
yann@492
   402
    # connecting, so force a global ${CT_CONNECT_TIMEOUT}-second timeout.
yann@2204
   403
    # For curl, no good progress indicator is available. So, be silent.
yann@2205
   404
    if CT_DoExecLog ALL "${_curl}" --ftp-pasv    --retry 3 --connect-timeout ${CT_CONNECT_TIMEOUT} -L -f -s -o "${tmp}"   "$1"  \
yann@2205
   405
    || CT_DoExecLog ALL "${_curl}"               --retry 3 --connect-timeout ${CT_CONNECT_TIMEOUT} -L -f -s -o "${tmp}"   "$1"  \
yann@2205
   406
    || CT_DoExecLog ALL "${_wget}" --passive-ftp --tries=3 -T ${CT_CONNECT_TIMEOUT} -nc --progress=dot:binary -O "${tmp}" "$1"  \
yann@2205
   407
    || CT_DoExecLog ALL "${_wget}"               --tries=3 -T ${CT_CONNECT_TIMEOUT} -nc --progress=dot:binary -O "${tmp}" "$1"  \
yann@2205
   408
    ; then
yann@2205
   409
        # One of them succeeded, good!
yann@2205
   410
        mv "${tmp}" "${dest}"
yann@2205
   411
    else
yann@2205
   412
        # Woops...
yann@2205
   413
        rm -f "${tmp}"
yann@2205
   414
    fi
yann@63
   415
}
yann@63
   416
yann@1113
   417
# This function tries to retrieve a tarball form a local directory
yann@1113
   418
# Usage: CT_GetLocal <basename> [.extension]
yann@1113
   419
CT_GetLocal() {
yann@1113
   420
    local basename="$1"
yann@1113
   421
    local first_ext="$2"
yann@1113
   422
    local ext
yann@1113
   423
yann@1113
   424
    # Do we already have it in *our* tarballs dir?
yann@1690
   425
    if ext="$( CT_GetFileExtension "${basename}" ${first_ext} )"; then
yann@1113
   426
        CT_DoLog DEBUG "Already have '${basename}'"
yann@1113
   427
        return 0
yann@1113
   428
    fi
yann@1113
   429
yann@1113
   430
    if [ -n "${CT_LOCAL_TARBALLS_DIR}" ]; then
yann@1113
   431
        CT_DoLog DEBUG "Trying to retrieve an already downloaded copy of '${basename}'"
yann@1113
   432
        # We'd rather have a bzip2'ed tarball, then gzipped tarball, plain tarball,
yann@1113
   433
        # or, as a failover, a file without extension.
yann@1113
   434
        for ext in ${first_ext} .tar.bz2 .tar.gz .tgz .tar ''; do
yann@1113
   435
            CT_DoLog DEBUG "Trying '${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}'"
yann@1113
   436
            if [ -r "${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}" -a \
yann@1113
   437
                 "${CT_FORCE_DOWNLOAD}" != "y" ]; then
yann@1113
   438
                CT_DoLog DEBUG "Got '${basename}' from local storage"
yann@1113
   439
                CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}" "${CT_TARBALLS_DIR}/${basename}${ext}"
yann@1113
   440
                return 0
yann@1113
   441
            fi
yann@1113
   442
        done
yann@1113
   443
    fi
yann@1113
   444
    return 1
yann@1113
   445
}
yann@1113
   446
yann@1113
   447
# This function saves the specified to local storage if possible,
yann@1113
   448
# and if so, symlinks it for later usage
yann@1113
   449
# Usage: CT_SaveLocal </full/path/file.name>
yann@1113
   450
CT_SaveLocal() {
yann@1113
   451
    local file="$1"
yann@1113
   452
    local basename="${file##*/}"
yann@1113
   453
yann@1113
   454
    if [ "${CT_SAVE_TARBALLS}" = "y" ]; then
yann@1134
   455
        CT_DoLog EXTRA "Saving '${basename}' to local storage"
yann@1113
   456
        # The file may already exist if downloads are forced: remove it first
yann@1113
   457
        CT_DoExecLog ALL rm -f "${CT_LOCAL_TARBALLS_DIR}/${basename}"
yann@1113
   458
        CT_DoExecLog ALL mv -f "${file}" "${CT_LOCAL_TARBALLS_DIR}"
yann@1113
   459
        CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${basename}" "${file}"
yann@1113
   460
    fi
yann@1113
   461
}
yann@1113
   462
yann@63
   463
# Download the file from one of the URLs passed as argument
yann@1113
   464
# Usage: CT_GetFile <basename> [.extension] <url> [url ...]
yann@63
   465
CT_GetFile() {
yann@63
   466
    local ext
yann@1179
   467
    local url URLS LAN_URLS
yann@63
   468
    local file="$1"
yann@1113
   469
    local first_ext
yann@63
   470
    shift
yann@712
   471
    # If next argument starts with a dot, then this is not an URL,
yann@712
   472
    # and we can consider that it is a preferred extension.
yann@242
   473
    case "$1" in
yann@712
   474
        .*) first_ext="$1"
yann@242
   475
            shift
yann@242
   476
            ;;
yann@242
   477
    esac
yann@63
   478
yann@1113
   479
    # Does it exist localy?
yann@1113
   480
    CT_GetLocal "${file}" ${first_ext} && return 0 || true
yann@1113
   481
    # No, it does not...
yann@63
   482
yann@754
   483
    # Try to retrieve the file
yann@788
   484
    CT_DoLog EXTRA "Retrieving '${file}'"
yann@63
   485
    CT_Pushd "${CT_TARBALLS_DIR}"
yann@754
   486
yann@1179
   487
    URLS="$@"
yann@1179
   488
yann@1022
   489
    # Add URLs on the LAN mirror
yann@1022
   490
    LAN_URLS=
yann@1022
   491
    if [ "${CT_USE_MIRROR}" = "y" ]; then
yann@1294
   492
        CT_TestOrAbort "Please set the mirror base URL" -n "${CT_MIRROR_BASE_URL}"
yann@1294
   493
        LAN_URLS="${LAN_URLS} ${CT_MIRROR_BASE_URL}/${file%-*}"
yann@1294
   494
        LAN_URLS="${LAN_URLS} ${CT_MIRROR_BASE_URL}"
yann@695
   495
yann@1134
   496
        if [ "${CT_PREFER_MIRROR}" = "y" ]; then
yann@1134
   497
            CT_DoLog DEBUG "Pre-pending LAN mirror URLs"
yann@1179
   498
            URLS="${LAN_URLS} ${URLS}"
yann@1134
   499
        else
yann@1134
   500
            CT_DoLog DEBUG "Appending LAN mirror URLs"
yann@1179
   501
            URLS="${URLS} ${LAN_URLS}"
yann@1134
   502
        fi
yann@1022
   503
    fi
yann@1022
   504
yann@1022
   505
    # Scan all URLs in turn, and try to grab a tarball from there
yann@1763
   506
    # Do *not* try git trees (ext=/.git), this is handled in a specific
yann@1763
   507
    # wrapper, below
yann@242
   508
    for ext in ${first_ext} .tar.bz2 .tar.gz .tgz .tar ''; do
yann@102
   509
        # Try all urls in turn
yann@1022
   510
        for url in ${URLS}; do
yann@523
   511
            CT_DoLog DEBUG "Trying '${url}/${file}${ext}'"
yann@265
   512
            CT_DoGetFile "${url}/${file}${ext}"
yann@265
   513
            if [ -f "${file}${ext}" ]; then
yann@799
   514
                CT_DoLog DEBUG "Got '${file}' from the Internet"
yann@1113
   515
                CT_SaveLocal "${CT_TARBALLS_DIR}/${file}${ext}"
yann@265
   516
                return 0
yann@265
   517
            fi
yann@102
   518
        done
yann@102
   519
    done
yann@63
   520
    CT_Popd
yann@63
   521
yann@754
   522
    CT_Abort "Could not retrieve '${file}'."
yann@63
   523
}
yann@63
   524
yann@1113
   525
# Checkout from CVS, and build the associated tarball
yann@1113
   526
# The tarball will be called ${basename}.tar.bz2
yann@1113
   527
# Prerequisite: either the server does not require password,
yann@1113
   528
# or the user must already be logged in.
yann@1113
   529
# 'tag' is the tag to retrieve. Must be specified, but can be empty.
yann@1113
   530
# If dirname is specified, then module will be renamed to dirname
yann@1113
   531
# prior to building the tarball.
yann@1592
   532
# Usage: CT_GetCVS <basename> <url> <module> <tag> [dirname[=subdir]]
yann@1592
   533
# Note: if '=subdir' is given, then it is used instead of 'module'.
yann@1113
   534
CT_GetCVS() {
yann@1113
   535
    local basename="$1"
yann@1113
   536
    local uri="$2"
yann@1113
   537
    local module="$3"
yann@1113
   538
    local tag="${4:+-r ${4}}"
yann@1113
   539
    local dirname="$5"
yann@1113
   540
    local tmp_dir
yann@1113
   541
yann@1113
   542
    # Does it exist localy?
yann@1113
   543
    CT_GetLocal "${basename}" && return 0 || true
yann@1113
   544
    # No, it does not...
yann@1113
   545
yann@1113
   546
    CT_DoLog EXTRA "Retrieving '${basename}'"
yann@1113
   547
yann@1113
   548
    CT_MktempDir tmp_dir
yann@1113
   549
    CT_Pushd "${tmp_dir}"
yann@1113
   550
yann@1113
   551
    CT_DoExecLog ALL cvs -z 9 -d "${uri}" co -P ${tag} "${module}"
yann@1592
   552
    if [ -n "${dirname}" ]; then
yann@1592
   553
        case "${dirname}" in
yann@1592
   554
            *=*)
yann@1593
   555
                CT_DoExecLog DEBUG mv "${dirname#*=}" "${dirname%%=*}"
yann@1593
   556
                CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${dirname%%=*}"
yann@1592
   557
                ;;
yann@1592
   558
            *)
yann@1592
   559
                CT_DoExecLog ALL mv "${module}" "${dirname}"
yann@1592
   560
                CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${dirname:-${module}}"
yann@1592
   561
                ;;
yann@1592
   562
        esac
yann@1592
   563
    fi
yann@1113
   564
    CT_SaveLocal "${CT_TARBALLS_DIR}/${basename}.tar.bz2"
yann@1113
   565
yann@1113
   566
    CT_Popd
yann@1113
   567
    CT_DoExecLog ALL rm -rf "${tmp_dir}"
yann@1113
   568
}
yann@1113
   569
yann@1244
   570
# Check out from SVN, and build the associated tarball
yann@1244
   571
# The tarball will be called ${basename}.tar.bz2
yann@1244
   572
# Prerequisite: either the server does not require password,
yann@1244
   573
# or the user must already be logged in.
yann@1244
   574
# 'rev' is the revision to retrieve
yann@1244
   575
# Usage: CT_GetSVN <basename> <url> [rev]
yann@1244
   576
CT_GetSVN() {
yann@1244
   577
    local basename="$1"
yann@1244
   578
    local uri="$2"
yann@1244
   579
    local rev="$3"
yann@1244
   580
yann@1244
   581
    # Does it exist localy?
yann@1244
   582
    CT_GetLocal "${basename}" && return 0 || true
yann@1244
   583
    # No, it does not...
yann@1244
   584
yann@1244
   585
    CT_DoLog EXTRA "Retrieving '${basename}'"
yann@1244
   586
yann@1244
   587
    CT_MktempDir tmp_dir
yann@1244
   588
    CT_Pushd "${tmp_dir}"
yann@1244
   589
yann@1244
   590
    CT_DoExecLog ALL svn export ${rev:+-r ${rev}} "${uri}" "${basename}"
yann@1244
   591
    CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${basename}"
yann@1244
   592
    CT_SaveLocal "${CT_TARBALLS_DIR}/${basename}.tar.bz2"
yann@1244
   593
yann@1244
   594
    CT_Popd
yann@1244
   595
    CT_DoExecLog ALL rm -rf "${tmp_dir}"
yann@1244
   596
}
yann@1244
   597
yann@1763
   598
# Clone a git tree
yann@1763
   599
# Tries the given URLs in turn until one can get cloned. No tarball will be created.
yann@1763
   600
# Prerequisites: either the server does not require password,
yann@1763
   601
# or the user has already taken any action to authenticate to the server.
yann@1763
   602
# The cloned tree will *not* be stored in the local tarballs dir!
yann@1763
   603
# Usage: CT_GetGit <basename> <url [url ...]>
yann@1763
   604
CT_GetGit() {
yann@1763
   605
    local basename="$1"; shift
yann@1763
   606
    local url
yann@1763
   607
    local cloned=0
yann@1763
   608
yann@1763
   609
    # Do we have it in our tarballs dir?
yann@1763
   610
    if [ -d "${CT_TARBALLS_DIR}/${basename}/.git" ]; then
yann@1763
   611
        CT_DoLog EXTRA "Updating git tree '${basename}'"
yann@1763
   612
        CT_Pushd "${CT_TARBALLS_DIR}/${basename}"
yann@1763
   613
        CT_DoExecLog ALL git pull
yann@1763
   614
        CT_Popd
yann@1763
   615
    else
yann@1763
   616
        CT_DoLog EXTRA "Retrieving git tree '${basename}'"
yann@1763
   617
        for url in "${@}"; do
yann@1763
   618
            CT_DoLog ALL "Trying to clone from '${url}'"
yann@1763
   619
            CT_DoForceRmdir "${CT_TARBALLS_DIR}/${basename}"
yann@1763
   620
            if git clone "${url}" "${CT_TARBALLS_DIR}/${basename}" 2>&1 |CT_DoLog ALL; then
yann@1763
   621
                cloned=1
yann@1763
   622
                break
yann@1763
   623
            fi
yann@1763
   624
        done
yann@1763
   625
        CT_TestOrAbort "Could not clone '${basename}'" ${cloned} -ne 0
yann@1763
   626
    fi
yann@1763
   627
}
yann@1763
   628
yann@1126
   629
# Extract a tarball
yann@63
   630
# Some tarballs need to be extracted in specific places. Eg.: glibc addons
yann@63
   631
# must be extracted in the glibc directory; uCLibc locales must be extracted
yann@1123
   632
# in the extra/locale sub-directory of uClibc. This is taken into account
yann@1123
   633
# by the caller, that did a 'cd' into the correct path before calling us
yann@1123
   634
# and sets nochdir to 'nochdir'.
yann@1763
   635
# Note also that this function handles the git trees!
yann@1763
   636
# Usage: CT_Extract <basename> [nochdir] [options]
yann@1763
   637
# where 'options' are dependent on the source (eg. git branch/tag...)
yann@1126
   638
CT_Extract() {
yann@1761
   639
    local nochdir="$1"
yann@1761
   640
    local basename
yann@1690
   641
    local ext
yann@1690
   642
yann@1761
   643
    if [ "${nochdir}" = "nochdir" ]; then
yann@1761
   644
        shift
yann@1761
   645
        nochdir="$(pwd)"
yann@1761
   646
    else
yann@1761
   647
        nochdir="${CT_SRC_DIR}"
yann@1761
   648
    fi
yann@1761
   649
yann@1761
   650
    basename="$1"
yann@1761
   651
    shift
yann@1761
   652
yann@1690
   653
    if ! ext="$(CT_GetFileExtension "${basename}")"; then
yann@1763
   654
      CT_Abort "'${basename}' not found in '${CT_TARBALLS_DIR}'"
yann@1690
   655
    fi
yann@1126
   656
    local full_file="${CT_TARBALLS_DIR}/${basename}${ext}"
yann@1126
   657
yann@1718
   658
    # Check if already extracted
yann@1718
   659
    if [ -e "${CT_SRC_DIR}/.${basename}.extracted" ]; then
yann@1718
   660
        CT_DoLog DEBUG "Already extracted '${basename}'"
yann@1718
   661
        return 0
yann@1718
   662
    fi
yann@1718
   663
yann@1691
   664
    # Check if previously partially extracted
yann@1691
   665
    if [ -e "${CT_SRC_DIR}/.${basename}.extracting" ]; then
yann@1691
   666
        CT_DoLog ERROR "The '${basename}' sources were partially extracted."
yann@1691
   667
        CT_DoLog ERROR "Please remove first:"
yann@1691
   668
        CT_DoLog ERROR " - the source dir for '${basename}', in '${CT_SRC_DIR}'"
yann@1691
   669
        CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${basename}.extracting'"
yann@1691
   670
        CT_Abort "I'll stop now to avoid any carnage..."
yann@1691
   671
    fi
yann@1691
   672
    CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${basename}.extracting"
yann@1691
   673
yann@1761
   674
    CT_Pushd "${nochdir}"
yann@63
   675
yann@1126
   676
    CT_DoLog EXTRA "Extracting '${basename}'"
yann@63
   677
    case "${ext}" in
anthony@2154
   678
        .tar.bz2)     CT_DoExecLog FILE tar xvjf "${full_file}";;
anthony@2154
   679
        .tar.gz|.tgz) CT_DoExecLog FILE tar xvzf "${full_file}";;
anthony@2154
   680
        .tar)         CT_DoExecLog FILE tar xvf  "${full_file}";;
yann@1763
   681
        /.git)        CT_ExtractGit "${basename}" "${@}";;
yann@1763
   682
        *)            CT_Abort "Don't know how to handle '${basename}${ext}': unknown extension";;
yann@63
   683
    esac
yann@63
   684
yann@1763
   685
    # Don't mark as being extracted for git
yann@1763
   686
    case "${ext}" in
yann@1763
   687
        /.git)  ;;
yann@1763
   688
        *)      CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${basename}.extracted";;
yann@1763
   689
    esac
yann@1691
   690
    CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/.${basename}.extracting"
yann@1126
   691
yann@1761
   692
    CT_Popd
yann@1126
   693
}
yann@1126
   694
yann@1763
   695
# Create a working git clone
yann@1763
   696
# Usage: CT_ExtractGit <basename> [ref]
yann@1763
   697
# where 'ref' is the reference to use:
yann@1763
   698
#   the full name of a branch, like "remotes/origin/branch_name"
yann@1763
   699
#   a date as understandable by git, like "YYYY-MM-DD[ hh[:mm[:ss]]]"
yann@1763
   700
#   a tag name
yann@1763
   701
CT_ExtractGit() {
yann@1763
   702
    local basename="${1}"
yann@1763
   703
    local ref="${2}"
yann@1763
   704
    local clone_dir
yann@1763
   705
    local ref_type
yann@1763
   706
yann@1763
   707
    # pushd now to be able to get git revlist in case ref is a date
yann@1763
   708
    clone_dir="${CT_TARBALLS_DIR}/${basename}"
yann@1763
   709
    CT_Pushd "${clone_dir}"
yann@1763
   710
yann@1763
   711
    # What kind of reference is ${ref} ?
yann@1763
   712
    if [ -z "${ref}" ]; then
yann@1763
   713
        # Don't update the clone, keep as-is
yann@1763
   714
        ref_type=none
yann@1763
   715
    elif git tag |grep -E "^${ref}$" >/dev/null 2>&1; then
yann@1763
   716
        ref_type=tag
yann@1763
   717
    elif git branch -a --no-color |grep -E "^. ${ref}$" >/dev/null 2>&1; then
yann@1763
   718
        ref_type=branch
yann@1763
   719
    elif date -d "${ref}" >/dev/null 2>&1; then
yann@1763
   720
        ref_type=date
yann@1763
   721
        ref=$(git rev-list -n1 --before="${ref}")
yann@1763
   722
    else
yann@1763
   723
        CT_Abort "Reference '${ref}' is an incorrect git reference: neither tag, branch nor date"
yann@1763
   724
    fi
yann@1763
   725
yann@1763
   726
    CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/${basename}"
yann@1763
   727
    CT_DoExecLog ALL ln -sf "${clone_dir}" "${CT_SRC_DIR}/${basename}"
yann@1763
   728
yann@1763
   729
    case "${ref_type}" in
yann@1763
   730
        none)   ;;
anthony@2154
   731
        *)      CT_DoExecLog FILE git checkout "${ref}";;
yann@1763
   732
    esac
yann@1763
   733
yann@1763
   734
    CT_Popd
yann@1763
   735
}
yann@1763
   736
yann@1126
   737
# Patches the specified component
yann@1761
   738
# See CT_Extract, above, for explanations on 'nochdir'
yann@1901
   739
# Usage: CT_Patch [nochdir] <packagename> <packageversion>
yann@1901
   740
# If the package directory is *not* packagename-packageversion, then
yann@1901
   741
# the caller must cd into the proper directory first, and call us
yann@1901
   742
# with nochdir
yann@1126
   743
CT_Patch() {
yann@1761
   744
    local nochdir="$1"
yann@1901
   745
    local pkgname
yann@1901
   746
    local version
yann@1901
   747
    local pkgdir
yann@1761
   748
    local base_file
yann@1761
   749
    local ver_file
yann@1507
   750
    local d
yann@1507
   751
    local -a patch_dirs
yann@1507
   752
    local bundled_patch_dir
yann@1507
   753
    local local_patch_dir
yann@1126
   754
yann@1761
   755
    if [ "${nochdir}" = "nochdir" ]; then
yann@1761
   756
        shift
yann@1906
   757
        pkgname="$1"
yann@1906
   758
        version="$2"
yann@1906
   759
        pkgdir="${pkgname}-${version}"
yann@1761
   760
        nochdir="$(pwd)"
yann@1761
   761
    else
yann@1906
   762
        pkgname="$1"
yann@1906
   763
        version="$2"
yann@1906
   764
        pkgdir="${pkgname}-${version}"
yann@1901
   765
        nochdir="${CT_SRC_DIR}/${pkgdir}"
yann@1761
   766
    fi
yann@1761
   767
yann@1126
   768
    # Check if already patched
yann@1901
   769
    if [ -e "${CT_SRC_DIR}/.${pkgdir}.patched" ]; then
yann@1901
   770
        CT_DoLog DEBUG "Already patched '${pkgdir}'"
yann@1126
   771
        return 0
yann@63
   772
    fi
yann@63
   773
yann@1271
   774
    # Check if already partially patched
yann@1901
   775
    if [ -e "${CT_SRC_DIR}/.${pkgdir}.patching" ]; then
yann@1901
   776
        CT_DoLog ERROR "The '${pkgdir}' sources were partially patched."
yann@1271
   777
        CT_DoLog ERROR "Please remove first:"
yann@1901
   778
        CT_DoLog ERROR " - the source dir for '${pkgdir}', in '${CT_SRC_DIR}'"
yann@1901
   779
        CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${pkgdir}.extracted'"
yann@1901
   780
        CT_DoLog ERROR " - the file '${CT_SRC_DIR}/.${pkgdir}.patching'"
yann@1271
   781
        CT_Abort "I'll stop now to avoid any carnage..."
yann@1271
   782
    fi
yann@1901
   783
    touch "${CT_SRC_DIR}/.${pkgdir}.patching"
yann@1271
   784
yann@1761
   785
    CT_Pushd "${nochdir}"
yann@1123
   786
yann@1901
   787
    CT_DoLog EXTRA "Patching '${pkgdir}'"
yann@63
   788
yann@1901
   789
    bundled_patch_dir="${CT_LIB_DIR}/patches/${pkgname}/${version}"
yann@1901
   790
    local_patch_dir="${CT_LOCAL_PATCH_DIR}/${pkgname}/${version}"
yann@1507
   791
yann@1507
   792
    case "${CT_PATCH_ORDER}" in
yann@1507
   793
        bundled)        patch_dirs=("${bundled_patch_dir}");;
yann@1507
   794
        local)          patch_dirs=("${local_patch_dir}");;
yann@1507
   795
        bundled,local)  patch_dirs=("${bundled_patch_dir}" "${local_patch_dir}");;
yann@1508
   796
        local,bundled)  patch_dirs=("${local_patch_dir}" "${bundled_patch_dir}");;
yann@1630
   797
        none)           patch_dirs=;;
yann@1507
   798
    esac
yann@1507
   799
yann@1507
   800
    for d in "${patch_dirs[@]}"; do
yann@1507
   801
        CT_DoLog DEBUG "Looking for patches in '${d}'..."
yann@1507
   802
        if [ -n "${d}" -a -d "${d}" ]; then
yann@1507
   803
            for p in "${d}"/*.patch; do
yann@63
   804
                if [ -f "${p}" ]; then
yann@523
   805
                    CT_DoLog DEBUG "Applying patch '${p}'"
yann@1765
   806
                    CT_DoExecLog ALL patch --no-backup-if-mismatch -g0 -F1 -p1 -f <"${p}"
yann@63
   807
                fi
yann@63
   808
            done
yann@1509
   809
            if [ "${CT_PATCH_SINGLE}" = "y" ]; then
yann@1509
   810
                break
yann@1509
   811
            fi
yann@63
   812
        fi
yann@63
   813
    done
yann@63
   814
yann@508
   815
    if [ "${CT_OVERIDE_CONFIG_GUESS_SUB}" = "y" ]; then
yann@508
   816
        CT_DoLog ALL "Overiding config.guess and config.sub"
yann@508
   817
        for cfg in config_guess config_sub; do
yann@1101
   818
            eval ${cfg}="${CT_LIB_DIR}/scripts/${cfg/_/.}"
yann@1101
   819
            [ -e "${CT_TOP_DIR}/scripts/${cfg/_/.}" ] && eval ${cfg}="${CT_TOP_DIR}/scripts/${cfg/_/.}"
yann@776
   820
            # Can't use CT_DoExecLog because of the '{} \;' to be passed un-mangled to find
yann@508
   821
            find . -type f -name "${cfg/_/.}" -exec cp -v "${!cfg}" {} \; |CT_DoLog ALL
yann@508
   822
        done
yann@508
   823
    fi
yann@508
   824
yann@1903
   825
    CT_DoExecLog DEBUG touch "${CT_SRC_DIR}/.${pkgdir}.patched"
yann@1903
   826
    CT_DoExecLog DEBUG rm -f "${CT_SRC_DIR}/.${pkgdir}.patching"
yann@1126
   827
yann@1761
   828
    CT_Popd
yann@63
   829
}
yann@63
   830
yann@182
   831
# Two wrappers to call config.(guess|sub) either from CT_TOP_DIR or CT_LIB_DIR.
yann@182
   832
# Those from CT_TOP_DIR, if they exist, will be be more recent than those from CT_LIB_DIR.
yann@182
   833
CT_DoConfigGuess() {
yann@1101
   834
    if [ -x "${CT_TOP_DIR}/scripts/config.guess" ]; then
yann@1101
   835
        "${CT_TOP_DIR}/scripts/config.guess"
yann@182
   836
    else
yann@1101
   837
        "${CT_LIB_DIR}/scripts/config.guess"
yann@182
   838
    fi
yann@182
   839
}
yann@182
   840
yann@182
   841
CT_DoConfigSub() {
yann@1101
   842
    if [ -x "${CT_TOP_DIR}/scripts/config.sub" ]; then
yann@1101
   843
        "${CT_TOP_DIR}/scripts/config.sub" "$@"
yann@182
   844
    else
yann@1101
   845
        "${CT_LIB_DIR}/scripts/config.sub" "$@"
yann@182
   846
    fi
yann@182
   847
}
yann@182
   848
yann@335
   849
# Compute the target tuple from what is provided by the user
yann@335
   850
# Usage: CT_DoBuildTargetTuple
yann@63
   851
# In fact this function takes the environment variables to build the target
yann@335
   852
# tuple. It is needed both by the normal build sequence, as well as the
yann@63
   853
# sample saving sequence.
yann@335
   854
CT_DoBuildTargetTuple() {
yann@383
   855
    # Set the endianness suffix, and the default endianness gcc option
yann@63
   856
    case "${CT_ARCH_BE},${CT_ARCH_LE}" in
yann@383
   857
        y,) target_endian_eb=eb
yann@383
   858
            target_endian_el=
yann@391
   859
            CT_ARCH_ENDIAN_CFLAG="-mbig-endian"
yann@527
   860
            CT_ARCH_ENDIAN_LDFLAG="-EB"
yann@383
   861
            ;;
yann@383
   862
        ,y) target_endian_eb=
yann@383
   863
            target_endian_el=el
yann@391
   864
            CT_ARCH_ENDIAN_CFLAG="-mlittle-endian"
yann@527
   865
            CT_ARCH_ENDIAN_LDFLAG="-EL"
yann@383
   866
            ;;
yann@63
   867
    esac
yann@383
   868
yann@965
   869
    # Build the default architecture tuple part
yann@965
   870
    CT_TARGET_ARCH="${CT_ARCH}"
yann@965
   871
yann@383
   872
    # Set defaults for the system part of the tuple. Can be overriden
yann@383
   873
    # by architecture-specific values.
yann@383
   874
    case "${CT_LIBC}" in
yann@787
   875
        *glibc) CT_TARGET_SYS=gnu;;
yann@383
   876
        uClibc) CT_TARGET_SYS=uclibc;;
yann@1591
   877
        *)      CT_TARGET_SYS=elf;;
yann@63
   878
    esac
yann@383
   879
yann@391
   880
    # Set the default values for ARCH, ABI, CPU, TUNE, FPU and FLOAT
yann@497
   881
    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
yann@497
   882
    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
yann@391
   883
    [ "${CT_ARCH_ARCH}"     ] && { CT_ARCH_ARCH_CFLAG="-march=${CT_ARCH_ARCH}";  CT_ARCH_WITH_ARCH="--with-arch=${CT_ARCH_ARCH}"; }
yann@391
   884
    [ "${CT_ARCH_ABI}"      ] && { CT_ARCH_ABI_CFLAG="-mabi=${CT_ARCH_ABI}";     CT_ARCH_WITH_ABI="--with-abi=${CT_ARCH_ABI}";    }
yann@391
   885
    [ "${CT_ARCH_CPU}"      ] && { CT_ARCH_CPU_CFLAG="-mcpu=${CT_ARCH_CPU}";     CT_ARCH_WITH_CPU="--with-cpu=${CT_ARCH_CPU}";    }
yann@405
   886
    [ "${CT_ARCH_TUNE}"     ] && { CT_ARCH_TUNE_CFLAG="-mtune=${CT_ARCH_TUNE}";  CT_ARCH_WITH_TUNE="--with-tune=${CT_ARCH_TUNE}"; }
yann@391
   887
    [ "${CT_ARCH_FPU}"      ] && { CT_ARCH_FPU_CFLAG="-mfpu=${CT_ARCH_FPU}";     CT_ARCH_WITH_FPU="--with-fpu=${CT_ARCH_FPU}";    }
yann@412
   888
    [ "${CT_ARCH_FLOAT_SW}" ] && { CT_ARCH_FLOAT_CFLAG="-msoft-float";           CT_ARCH_WITH_FLOAT="--with-float=soft";          }
yann@391
   889
yann@965
   890
    # Build the default kernel tuple part
yann@965
   891
    CT_TARGET_KERNEL="${CT_KERNEL}"
yann@964
   892
yann@965
   893
    # Overide the default values with the components specific settings
yann@964
   894
    CT_DoArchTupleValues
yann@965
   895
    CT_DoKernelTupleValues
yann@383
   896
yann@391
   897
    # Finish the target tuple construction
bartvdrmeulen@1896
   898
    CT_TARGET="${CT_TARGET_ARCH}"
bartvdrmeulen@1896
   899
    CT_TARGET="${CT_TARGET}${CT_TARGET_VENDOR:+-${CT_TARGET_VENDOR}}"
bartvdrmeulen@1896
   900
    CT_TARGET="${CT_TARGET}${CT_TARGET_KERNEL:+-${CT_TARGET_KERNEL}}"
bartvdrmeulen@1896
   901
    CT_TARGET="${CT_TARGET}${CT_TARGET_SYS:+-${CT_TARGET_SYS}}"
yann@1094
   902
yann@1094
   903
    # Sanity checks
yann@1094
   904
    __sed_alias=""
yann@1094
   905
    if [ -n "${CT_TARGET_ALIAS_SED_EXPR}" ]; then
yann@1094
   906
        __sed_alias=$(echo "${CT_TARGET}" |sed -r -e "${CT_TARGET_ALIAS_SED_EXPR}")
yann@1094
   907
    fi
yann@1094
   908
    case ":${CT_TARGET_VENDOR}:${CT_TARGET_ALIAS}:${__sed_alias}:" in
yann@1094
   909
      :*" "*:*:*:) CT_Abort "Don't use spaces in the vendor string, it breaks things.";;
yann@1094
   910
      :*"-"*:*:*:) CT_Abort "Don't use dashes in the vendor string, it breaks things.";;
yann@1094
   911
      :*:*" "*:*:) CT_Abort "Don't use spaces in the target alias, it breaks things.";;
yann@1094
   912
      :*:*:*" "*:) CT_Abort "Don't use spaces in the target sed transform, it breaks things.";;
yann@1094
   913
    esac
yann@1094
   914
yann@1094
   915
    # Canonicalise it
yann@1094
   916
    CT_TARGET=$(CT_DoConfigSub "${CT_TARGET}")
yann@391
   917
    # Prepare the target CFLAGS
yann@767
   918
    CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ENDIAN_CFLAG}"
yann@527
   919
    CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ARCH_CFLAG}"
yann@397
   920
    CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ABI_CFLAG}"
yann@397
   921
    CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_CPU_CFLAG}"
yann@397
   922
    CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_TUNE_CFLAG}"
yann@397
   923
    CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FPU_CFLAG}"
yann@397
   924
    CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FLOAT_CFLAG}"
yann@527
   925
yann@527
   926
    # Now on for the target LDFLAGS
yann@767
   927
    CT_ARCH_TARGET_LDFLAGS="${CT_ARCH_TARGET_LDFLAGS} ${CT_ARCH_ENDIAN_LDFLAG}"
yann@63
   928
}
yann@121
   929
yann@121
   930
# This function does pause the build until the user strikes "Return"
yann@121
   931
# Usage: CT_DoPause [optional_message]
yann@121
   932
CT_DoPause() {
yann@121
   933
    local foo
yann@121
   934
    local message="${1:-Pausing for your pleasure}"
yann@121
   935
    CT_DoLog INFO "${message}"
yann@523
   936
    read -p "Press 'Enter' to continue, or Ctrl-C to stop..." foo >&6
yann@121
   937
    return 0
yann@121
   938
}
yann@121
   939
yann@1907
   940
# This function creates a tarball of the specified directory, but
yann@1907
   941
# only if it exists
yann@1907
   942
# Usage: CT_DoTarballIfExists <dir> <tarball_basename> [extra_tar_options [...]]
yann@1907
   943
CT_DoTarballIfExists() {
yann@1907
   944
    local dir="$1"
yann@1907
   945
    local tarball="$2"
yann@1907
   946
    shift 2
yann@1907
   947
    local -a extra_tar_opts=( "$@" )
yann@1908
   948
    local -a compress
yann@1907
   949
yann@1907
   950
    case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
yann@1908
   951
        y)  compress=( gzip -c -3 - ); tar_ext=.gz;;
yann@1908
   952
        *)  compress=( cat - );        tar_ext=;;
yann@1907
   953
    esac
yann@1907
   954
yann@1907
   955
    if [ -d "${dir}" ]; then
yann@1907
   956
        CT_DoLog DEBUG "  Saving '${dir}'"
yann@1908
   957
        { tar c -C "${dir}" -v -f - "${extra_tar_opts[@]}" .    \
yann@1908
   958
          |"${compress[@]}" >"${tarball}.tar${tar_ext}"         ;
anthony@2155
   959
        } 2>&1 |sed -r -e 's/^/    /;' |CT_DoLog STATE
yann@1907
   960
    else
anthony@2155
   961
        CT_DoLog STATE "  Not saving '${dir}': does not exist"
yann@1907
   962
    fi
yann@1907
   963
}
yann@1907
   964
yann@1907
   965
# This function extracts a tarball to the specified directory, but
yann@1907
   966
# only if the tarball exists
anthony@2155
   967
# Usage: CT_DoExtractTarballIfExists <tarball_basename> <dir> [extra_tar_options [...]]
yann@1907
   968
CT_DoExtractTarballIfExists() {
yann@1907
   969
    local tarball="$1"
yann@1907
   970
    local dir="$2"
yann@1907
   971
    shift 2
yann@1907
   972
    local -a extra_tar_opts=( "$@" )
yann@1908
   973
    local -a uncompress
yann@1907
   974
yann@1907
   975
    case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
yann@1908
   976
        y)  uncompress=( gzip -c -d ); tar_ext=.gz;;
yann@1908
   977
        *)  uncompress=( cat );        tar_ext=;;
yann@1907
   978
    esac
yann@1907
   979
yann@1907
   980
    if [ -f "${tarball}.tar${tar_ext}" ]; then
yann@1907
   981
        CT_DoLog DEBUG "  Restoring '${dir}'"
yann@1907
   982
        CT_DoForceRmdir "${dir}"
yann@1907
   983
        CT_DoExecLog DEBUG mkdir -p "${dir}"
yann@1908
   984
        { "${uncompress[@]}" "${tarball}.tar${tar_ext}"     \
yann@1908
   985
          |tar x -C "${dir}" -v -f - "${extra_tar_opts[@]}" ;
anthony@2155
   986
        } 2>&1 |sed -r -e 's/^/    /;' |CT_DoLog STATE
yann@1907
   987
    else
anthony@2155
   988
        CT_DoLog STATE "  Not restoring '${dir}': does not exist"
yann@1907
   989
    fi
yann@1907
   990
}
yann@1907
   991
yann@121
   992
# This function saves the state of the toolchain to be able to restart
yann@121
   993
# at any one point
yann@121
   994
# Usage: CT_DoSaveState <next_step_name>
yann@121
   995
CT_DoSaveState() {
yann@121
   996
	[ "${CT_DEBUG_CT_SAVE_STEPS}" = "y" ] || return 0
yann@121
   997
    local state_name="$1"
yann@121
   998
    local state_dir="${CT_STATE_DIR}/${state_name}"
yann@121
   999
yann@1266
  1000
    # Log this to the log level required by the user
yann@1266
  1001
    CT_DoLog ${CT_LOG_LEVEL_MAX} "Saving state to restart at step '${state_name}'..."
yann@1134
  1002
yann@121
  1003
    rm -rf "${state_dir}"
yann@121
  1004
    mkdir -p "${state_dir}"
yann@121
  1005
anthony@2155
  1006
    CT_DoLog STATE "  Saving environment and aliases"
yann@738
  1007
    # We must omit shell functions, and some specific bash variables
yann@738
  1008
    # that break when restoring the environment, later. We could do
yann@1299
  1009
    # all the processing in the awk script, but a sed is easier...
yann@1299
  1010
    set |awk '
yann@1017
  1011
              BEGIN { _p = 1; }
yann@1017
  1012
              $0~/^[^ ]+ \(\)/ { _p = 0; }
yann@1017
  1013
              _p == 1
yann@1017
  1014
              $0 == "}" { _p = 1; }
yann@1017
  1015
              ' |sed -r -e '/^BASH_(ARGC|ARGV|LINENO|SOURCE|VERSINFO)=/d;
yann@738
  1016
                           /^(UID|EUID)=/d;
yann@738
  1017
                           /^(FUNCNAME|GROUPS|PPID|SHELLOPTS)=/d;' >"${state_dir}/env.sh"
yann@121
  1018
yann@2130
  1019
    if [ "${CT_COMPLIBS_BACKUP}" = "y" ]; then
yann@1907
  1020
        CT_DoTarballIfExists "${CT_COMPLIBS_DIR}" "${state_dir}/complibs_dir"
yann@1894
  1021
    fi
yann@2308
  1022
    CT_DoTarballIfExists "${CT_BUILDTOOLS_PREFIX_DIR}" "${state_dir}/buildtools_dir"
yann@1907
  1023
    CT_DoTarballIfExists "${CT_CONFIG_DIR}" "${state_dir}/config_dir"
yann@1907
  1024
    CT_DoTarballIfExists "${CT_CC_CORE_STATIC_PREFIX_DIR}" "${state_dir}/cc_core_static_prefix_dir"
yann@1907
  1025
    CT_DoTarballIfExists "${CT_CC_CORE_SHARED_PREFIX_DIR}" "${state_dir}/cc_core_shared_prefix_dir"
yann@1907
  1026
    CT_DoTarballIfExists "${CT_PREFIX_DIR}" "${state_dir}/prefix_dir" --exclude '*.log'
yann@121
  1027
yann@2339
  1028
    CT_DoLog STATE "  Saving log file"
yann@2339
  1029
    exec >/dev/null
yann@2339
  1030
    case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
yann@2339
  1031
        y)  gzip -3 -c "${tmp_log_file}"  >"${state_dir}/log.gz";;
yann@2339
  1032
        *)  cat "${tmp_log_file}" >"${state_dir}/log";;
yann@2339
  1033
    esac
yann@2339
  1034
    exec >>"${tmp_log_file}"
yann@121
  1035
}
yann@121
  1036
yann@136
  1037
# This function restores a previously saved state
yann@121
  1038
# Usage: CT_DoLoadState <state_name>
yann@121
  1039
CT_DoLoadState(){
yann@121
  1040
    local state_name="$1"
yann@121
  1041
    local state_dir="${CT_STATE_DIR}/${state_name}"
yann@135
  1042
    local old_RESTART="${CT_RESTART}"
yann@135
  1043
    local old_STOP="${CT_STOP}"
yann@121
  1044
yann@523
  1045
    CT_TestOrAbort "The previous build did not reach the point where it could be restarted at '${CT_RESTART}'" -d "${state_dir}"
yann@141
  1046
yann@121
  1047
    # We need to do something special with the log file!
yann@121
  1048
    if [ "${CT_LOG_TO_FILE}" = "y" ]; then
yann@121
  1049
        exec >"${state_dir}/tail.log"
yann@121
  1050
    fi
yann@1266
  1051
yann@1266
  1052
    # Log this to the log level required by the user
yann@1266
  1053
    CT_DoLog ${CT_LOG_LEVEL_MAX} "Restoring state at step '${state_name}', as requested."
yann@121
  1054
yann@1907
  1055
    CT_DoExtractTarballIfExists "${state_dir}/prefix_dir" "${CT_PREFIX_DIR}"
yann@1907
  1056
    CT_DoExtractTarballIfExists "${state_dir}/cc_core_shared_prefix_dir" "${CT_CC_CORE_SHARED_PREFIX_DIR}"
yann@1907
  1057
    CT_DoExtractTarballIfExists "${state_dir}/cc_core_static_prefix_dir" "${CT_CC_CORE_STATIC_PREFIX_DIR}"
yann@1907
  1058
    CT_DoExtractTarballIfExists "${state_dir}/config_dir" "${CT_CONFIG_DIR}"
yann@2308
  1059
    CT_DoExtractTarballIfExists "${state_dir}/buildtools_dir" "${CT_BUILDTOOLS_PREFIX_DIR}"
yann@2130
  1060
    if [ "${CT_COMPLIBS_BACKUP}" = "y" ]; then
yann@1907
  1061
        CT_DoExtractTarballIfExists "${state_dir}/complibs_dir" "${CT_COMPLIBS_DIR}"
yann@1894
  1062
    fi
yann@1894
  1063
yann@121
  1064
    # Restore the environment, discarding any error message
yann@121
  1065
    # (for example, read-only bash internals)
anthony@2155
  1066
    CT_DoLog STATE "  Restoring environment"
yann@121
  1067
    . "${state_dir}/env.sh" >/dev/null 2>&1 || true
yann@121
  1068
yann@135
  1069
    # Restore the new RESTART and STOP steps
yann@135
  1070
    CT_RESTART="${old_RESTART}"
yann@135
  1071
    CT_STOP="${old_STOP}"
yann@135
  1072
    unset old_stop old_restart
yann@135
  1073
yann@2339
  1074
    CT_DoLog STATE "  Restoring log file"
yann@2339
  1075
    exec >/dev/null
yann@2339
  1076
    case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
yann@2339
  1077
        y)  zcat "${state_dir}/log.gz" >"${tmp_log_file}";;
yann@2339
  1078
        *)  cat "${state_dir}/log" >"${tmp_log_file}";;
yann@2339
  1079
    esac
yann@2339
  1080
    cat "${state_dir}/tail.log" >>"${tmp_log_file}"
yann@2339
  1081
    exec >>"${tmp_log_file}"
yann@2339
  1082
    rm -f "${state_dir}/tail.log"
yann@121
  1083
}