scripts/functions
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Mon Mar 29 00:15:32 2010 +0200 (2010-03-29)
changeset 1864 758d5137fe87
parent 1765 93cb44d28002
child 1894 faea3afad5c0
permissions -rw-r--r--
scripts/populate: optimise search loop

Curently, populate will iterate over all ELF (shared objects|executables)
to look for missing NEEDED DSOs, adding to the list at every iterations
of the search loop.

Instead of looking again at previously handled ELF files, recursively
resolve every ELf files.

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