scripts/functions
author Remy Bohmer <linux@bohmer.net>
Thu May 27 23:18:19 2010 +0200 (2010-05-27)
changeset 2060 51e4597b07fc
parent 2044 20dd8cef1c8a
child 2079 d2ceb140371d
child 2092 18da1aa1beab
permissions -rw-r--r--
scripts: add option to strip all toolchain executables

To reduce filesizes of the toolchain and even improve build times
of projects to be build with this toolchain it is usefull to strip
the delivered toolchain executables. Since it is not likely that we
will debug the toolchain executables itself we do not need the
debug information inside the executables itself.

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