crosstool-ng/scripts/functions

1963 lines
71 KiB
Plaintext
Raw Normal View History

# -*- mode: sh; tab-width: 4 -*-
# vi: ts=4:sw=4:sts=4:et
# This file contains some useful common functions
# Copyright 2007 Yann E. MORIN
# Licensed under the GPL v2. See COPYING in the root of this package
CT_LoadConfig() {
local o oldvals vals
# Parse the configuration file
# It has some info about the logging facility, so include it early
# It also sets KERNEL/ARCH/... for file inclusion below. Does not handle
# recursive definitions yet. We don't need arrays at this point.
CT_TestOrAbort "Configuration file not found. Please create one." -r .config
. .config
# Include sub-scripts instead of calling them: that way, we do not have to
# export any variable, nor re-parse the configuration and functions files.
. "${CT_LIB_DIR}/scripts/build/internals.sh"
. "${CT_LIB_DIR}/scripts/build/arch.sh"
. "${CT_LIB_DIR}/scripts/build/companion_tools.sh"
. "${CT_LIB_DIR}/scripts/build/kernel/${CT_KERNEL}.sh"
. "${CT_LIB_DIR}/scripts/build/companion_libs.sh"
. "${CT_LIB_DIR}/scripts/build/binutils/${CT_BINUTILS}.sh"
. "${CT_LIB_DIR}/scripts/build/libc/${CT_LIBC}.sh"
. "${CT_LIB_DIR}/scripts/build/cc.sh"
. "${CT_LIB_DIR}/scripts/build/debug.sh"
. "${CT_LIB_DIR}/scripts/build/test_suite.sh"
# Target tuple: CT_TARGET needs a little love:
CT_DoBuildTargetTuple
# Kludge: If any of the configured options needs CT_TARGET,
# then rescan the options file now. This also handles recursive variables;
# but we don't want to loop forever if there's a circular reference.
oldvals=""
try=0
while [ "$try" -le 10 ]; do
. .config
vals=`set | ${grep} -E '^CT_'`
if [ "$oldvals" = "$vals" ]; then
break
fi
oldvals="$vals"
try=$[ try + 1 ]
done
if [ "$try" -gt 10 ]; then
CT_Abort "Variables in .config recurse too deep."
fi
# Double eval: first eval substitutes option name, second eval unescapes quotes
# and whitespace.
for o in `set | ${sed} -rn 's/^(CT_[A-Za-z0-9_]*_ARRAY)=.*/\1/p'`; do
eval "eval $o=(\"\$$o\")"
done
}
# Prepare the fault handler
CT_OnError() {
local ret=$?
local result
local old_trap
local intro
local file line func
local step step_depth
# To avoid printing the backtace for each sub-shell
# up to the top-level, just remember we've dumped it
if [ ! -f "${CT_WORK_DIR}/backtrace" ]; then
[ -d "${CT_WORK_DIR}" ] && touch "${CT_WORK_DIR}/backtrace"
# Print steps backtrace
step_depth=${CT_STEP_COUNT}
CT_STEP_COUNT=1 # To have a zero-indentation
CT_DoLog ERROR ""
CT_DoLog ERROR ">>"
intro="Build failed"
for((step=step_depth; step>0; step--)); do
CT_DoLog ERROR ">> ${intro} in step '${CT_STEP_MESSAGE[${step}]}'"
intro=" called"
done
# Print functions backtrace
intro="Error happened in"
CT_DoLog ERROR ">>"
for((depth=1; ${BASH_LINENO[$((${depth}-1))]}>0; depth++)); do
file="${BASH_SOURCE[${depth}]#${CT_LIB_DIR}/}"
func="${FUNCNAME[${depth}]}"
line="@${BASH_LINENO[${depth}-1]:-?}"
CT_DoLog ERROR ">> ${intro}: ${func}[${file}${line}]"
intro=" called from"
done
# If the user asked for interactive debugging, dump him/her to a shell
if [ "${CT_DEBUG_INTERACTIVE}" = "y" ]; then
# We do not want this sub-shell exit status to be caught, because
# it is absolutely legit that it exits with non-zero.
# Save the trap handler to restore it after our debug-shell
old_trap="$(trap -p ERR)"
trap -- ERR
(
CT_LogDisable # In this subshell
printf "\r \n\nCurrent command"
if [ -n "${cur_cmd}" ]; then
printf ":\n %s\n" "${cur_cmd}"
else
printf " (unknown), "
fi
printf "exited with error code: %d\n" ${ret}
printf "Please fix it up and finish by exiting the shell with one of these values:\n"
printf " 1 fixed, continue with next build command\n"
if [ -n "${cur_cmd}" ]; then
printf " 2 repeat this build command\n"
fi
printf " 3 abort build\n\n"
while true; do
${bash} --rcfile <(printf "PS1='ct-ng:\w> '\nPROMPT_COMMAND=''\n") -i
result=$?
case $result in
1) printf "\nContinuing past the failed command.\n\n"
break
;;
2) if [ -n "${cur_cmd}" ]; then
printf "\nRe-trying last command.\n\n"
break
fi
;;
3) break;;
esac
printf "\nPlease exit with one of these values:\n"
printf " 1 fixed, continue with next build command\n"
if [ -n "${cur_cmd}" ]; then
printf " 2 repeat this build command\n"
fi
printf " 3 abort build\n"
done
exit $result
)
result=$?
# Restore the trap handler
eval "${old_trap}"
case "${result}" in
1) rm -f "${CT_WORK_DIR}/backtrace"; touch "${CT_BUILD_DIR}/skip"; return;;
2) rm -f "${CT_WORK_DIR}/backtrace"; touch "${CT_BUILD_DIR}/repeat"; return;;
# 3 is an abort, continue...
esac
fi
fi
# And finally, in top-level shell, print some hints
if [ ${BASH_SUBSHELL} -eq 0 ]; then
# Help diagnose the error
CT_STEP_COUNT=1 # To have a zero-indentation
CT_DoLog ERROR ">>"
if [ "${CT_LOG_TO_FILE}" = "y" ]; then
CT_DoLog ERROR ">> For more info on this error, look at the file: '${CT_BUILD_LOG#${CT_TOP_DIR}/}'"
fi
CT_DoLog ERROR ">> There is a list of known issues, some with workarounds, in:"
CT_DoLog ERROR ">> '${CT_DOC_DIR#${CT_TOP_DIR}/}/B - Known issues.txt'"
CT_DoLog ERROR ">>"
CT_DoLog ERROR ">> If you feel this is a bug in crosstool-NG, report it at:"
CT_DoLog ERROR ">> https://github.com/crosstool-ng/crosstool-ng/issues/"
CT_DoLog ERROR ">>"
CT_DoLog ERROR ">> Make sure your report includes all the information pertinent to this issue."
CT_DoLog ERROR ">> Read the bug reporting guidelines here:"
CT_DoLog ERROR ">> http://crosstool-ng.github.io/support/"
CT_DoLog ERROR ""
CT_DoEnd ERROR
rm -f "${CT_WORK_DIR}/backtrace"
fi
exit $ret
}
# Install the fault handler
trap CT_OnError ERR
# Inherit the fault handler in subshells and functions
set -E
# Make pipes fail on the _first_ failed command
# Not supported on bash < 3.x, but we need it, so drop the obsoleting bash-2.x
set -o pipefail
# Don't hash commands' locations, and search every time it is requested.
# This is slow, but needed because of the static/shared core gcc which shall
# always match to shared if it exists, and only fallback to static if the
# shared is not found
set +o hashall
# Log policy:
# - first of all, save stdout so we can see the live logs: fd #6
# (also save stdin and stderr for use by CT_DEBUG_INTERACTIVE)
# FIXME: it doesn't look like anyone is overriding stdin/stderr. Do we need
# to save/restore them?
CT_LogEnable() {
local clean=no
local arg
for arg in "$@"; do eval "$arg"; done
exec 6>&1 7>&2 8<&0
CT_BUILD_LOG="${CT_TOP_DIR}/build.log"
CT_LOG_ENABLED=y
if [ "$clean" = "yes" ]; then
rm -f "${CT_BUILD_LOG}"
fi
exec >>"${CT_BUILD_LOG}"
}
# Restore original stdout, stderr and stdin
CT_LogDisable() {
exec >&6 2>&7 <&8
CT_LOG_ENABLED=
}
# The different log levels:
CT_LOG_LEVEL_ERROR=0
CT_LOG_LEVEL_WARN=1
CT_LOG_LEVEL_INFO=2
CT_LOG_LEVEL_EXTRA=3
CT_LOG_LEVEL_CFG=4
CT_LOG_LEVEL_FILE=5
CT_LOG_LEVEL_STATE=6
CT_LOG_LEVEL_ALL=7
CT_LOG_LEVEL_DEBUG=8
# Make it easy to use \n and !
CR=$(printf "\n")
BANG='!'
# A function to log what is happening
# Different log level are available:
# - ERROR: A serious, fatal error occurred
# - WARN: A non fatal, non serious error occurred, take your responsbility with the generated build
# - INFO: Informational messages
# - EXTRA: Extra informational messages
# - CFG: Output of various "./configure"-type scripts
# - FILE: File / archive unpacking.
# - STATE: State save & restore
# - ALL: Component's build messages
# - DEBUG: Internal debug messages
# Usage: CT_DoLog <level> [message]
# If message is empty, then stdin will be logged.
CT_DoLog() {
local max_level LEVEL level cur_l cur_L
local l
eval max_level="\${CT_LOG_LEVEL_${CT_LOG_LEVEL_MAX}}"
# Set the maximum log level to DEBUG if we have none
[ -z "${max_level}" ] && max_level=${CT_LOG_LEVEL_DEBUG}
LEVEL="$1"; shift
eval level="\${CT_LOG_LEVEL_${LEVEL}}"
if [ $# -eq 0 ]; then
cat -
else
echo -e "${*}"
fi |( IFS="${CR}" # We want the full lines, even leading spaces
_prog_bar_cpt=0
_prog_bar[0]='/'
_prog_bar[1]='-'
_prog_bar[2]='\'
_prog_bar[3]='|'
indent=$((2*CT_STEP_COUNT))
while read line; do
case "${CT_LOG_SEE_TOOLS_WARN},${line}" in
y,*"warning:"*) cur_L=WARN; cur_l=${CT_LOG_LEVEL_WARN};;
y,*"WARNING:"*) cur_L=WARN; cur_l=${CT_LOG_LEVEL_WARN};;
*"error:"*) cur_L=ERROR; cur_l=${CT_LOG_LEVEL_ERROR};;
*"make["*"]: ***"*) cur_L=ERROR; cur_l=${CT_LOG_LEVEL_ERROR};;
*) cur_L="${LEVEL}"; cur_l="${level}";;
esac
# There will always be a log file (stdout, fd #1), be it /dev/null
if [ -n "${CT_LOG_ENABLED}" ]; then
printf "[%-5s]%*s%s%s\n" "${cur_L}" "${indent}" " " "${line}"
# If log file has been set up, fd#6 is console and it only
# gets the most important messages.
if [ ${cur_l} -le ${max_level} ]; then
# Only print to console (fd #6) if log level is high enough.
printf "${CT_LOG_PROGRESS_BAR:+\r}[%-5s]%*s%s%s\n" "${cur_L}" "${indent}" " " "${line}" >&6
fi
if [ "${CT_LOG_PROGRESS_BAR}" = "y" ]; then
printf "\r[%02d:%02d] %s " $((SECONDS/60)) $((SECONDS%60)) "${_prog_bar[$((_prog_bar_cpt/10))]}" >&6
_prog_bar_cpt=$(((_prog_bar_cpt+1)%40))
fi
elif [ ${cur_l} -le ${CT_LOG_LEVEL_WARN} ]; then
printf "[%-5s]%*s%s%s\n" "${cur_L}" "${indent}" " " "${line}" >&2
fi
done
)
return 0
}
# Execute an action, and log its messages
# It is possible to even log local variable assignments (a-la: var=val ./cmd opts)
# Usage: CT_DoExecLog <level> [VAR=val...] <command> [parameters...]
CT_DoExecLog() {
local level="$1"
local cur_cmd
local ret
shift
(
for i in "$@"; do
cur_cmd+="'${i}' "
done
while true; do
case "${1}" in
*=*) eval export "'${1}'"; shift;;
*) break;;
esac
done
# This while-loop goes hand-in-hand with the ERR trap handler:
# - if the command terminates successfully, then we hit the break
# statement, and we exit the loop
# - if the command terminates in error, then the ERR handler kicks
# in, then:
# - if the user did *not* ask for interactive debugging, the ERR
# handler exits, and we hit the end of the sub-shell
# - if the user did ask for interactive debugging, the ERR handler
# spawns a shell. Upon termination of this shell, the ERR handler
# examines the exit status of the shell:
# - if 1, the ERR handler returns; then we hit the else statement,
# then the break, and we exit the 'while' loop, to continue the
# build;
# - if 2, the ERR handler touches the repeat file, and returns;
# then we hit the if statement, and we loop for one more
# iteration;
# - if 3, the ERR handler exits with the command's exit status,
# and we're dead;
# - for any other exit status of the shell, the ERR handler
# prints an informational message, and respawns the shell
#
# This allows a user to get an interactive shell that has the same
# environment (PATH and so on) that the failed command was ran with.
while true; do
rm -f "${CT_BUILD_DIR}/repeat"
CT_DoLog DEBUG "==> Executing: ${cur_cmd}"
"${@}" 2>&1 |CT_DoLog "${level}"
ret="${?}"
if [ -f "${CT_BUILD_DIR}/repeat" ]; then
rm -f "${CT_BUILD_DIR}/repeat"
continue
elif [ -f "${CT_BUILD_DIR}/skip" ]; then
rm -f "${CT_BUILD_DIR}/skip"
ret=0
break
else
break
fi
done
exit ${ret}
)
# Catch failure of the sub-shell
[ $? -eq 0 ]
}
# Tail message to be logged whatever happens
# Usage: CT_DoEnd <level>
CT_DoEnd()
{
local level="$1"
CT_STOP_DATE=$(CT_DoDate +%s%N)
CT_STOP_DATE_HUMAN=$(CT_DoDate +%Y%m%d.%H%M%S)
if [ "${level}" != "ERROR" ]; then
CT_DoLog "${level:-INFO}" "Build completed at ${CT_STOP_DATE_HUMAN}"
fi
elapsed=$((CT_STOP_DATE-CT_STAR_DATE))
elapsed_min=$((elapsed/(60*1000*1000*1000)))
elapsed_sec=$(printf "%02d" $(((elapsed%(60*1000*1000*1000))/(1000*1000*1000))))
elapsed_csec=$(printf "%02d" $(((elapsed%(1000*1000*1000))/(10*1000*1000))))
CT_DoLog ${level:-INFO} "(elapsed: ${elapsed_min}:${elapsed_sec}.${elapsed_csec})"
}
# Remove entries referring to . and other relative paths
# Usage: CT_SanitizePath
CT_SanitizePath() {
local new
local p
local IFS=:
for p in $PATH; do
# Only accept absolute paths;
# Note: as a special case the empty string in PATH is equivalent to .
if [ -n "${p}" -a -z "${p%%/*}" ]; then
new="${new}${new:+:}${p}"
fi
done
PATH="${new}"
}
# Sanitize the directory name contained in the variable passed as argument:
# - remove duplicate /
# - remove . (current dir) at the beginning, in the middle or at the end
# - resolve .. (parent dir) if there is a previous component
# - remove .. (parent dir) if at the root
#
# Usage: CT_SanitizeVarDir CT_PREFIX_DIR
CT_SanitizeVarDir() {
local var
local old_dir
local new_dir tmp
for var in "$@"; do
eval "old_dir=\"\${${var}}\""
new_dir=$( echo "${old_dir}" | ${awk} '
{
isabs = $1 == "" # Started with a slash
trail = $NF == "" # Ending with a slash
ncomp = 0 # Components in a path so far
for (i = 1; i <= NF; i++) {
# Double-slash or current dir? Ignore
if ($i == "" || $i == ".") {
continue;
}
# .. pops the last component unless it is at the beginning
if ($i == ".." && ncomp != 0 && comps[ncomp] != "..") {
ncomp--;
continue;
}
comps[++ncomp] = $i;
}
seencomp = 0
for (i = 1; i <= ncomp; i++) {
if (comps[i] == ".." && isabs) {
# /../ at the beginning is equivalent to /
continue;
}
printf "%s%s", isabs || i != 1 ? "/" : "", comps[i];
seencomp = 1;
}
if (!seencomp && !isabs && !trail) {
# Eliminated all components, but no trailing slash -
# if the result is appended with /foo, must not become absolute
printf ".";
}
if ((!seencomp && isabs) || (seencomp && trail)) {
printf "/";
}
}' FS=/ )
eval "${var}=\"${new_dir}\""
CT_DoLog DEBUG "Sanitized '${var}': '${old_dir}' -> '${new_dir}'"
done
}
# Abort the execution with an error message
# Usage: CT_Abort <message>
CT_Abort() {
CT_DoLog ERROR "$1"
false
}
# Test a condition, and print a message if satisfied
# Usage: CT_Test <message> <tests>
CT_Test() {
local ret
local m="$1"
shift
CT_DoLog DEBUG "Testing '! ( $* )'"
test "$@" && CT_DoLog WARN "$m"
return 0
}
# Test a condition, and abort with an error message if satisfied
# Usage: CT_TestAndAbort <message> <tests>
CT_TestAndAbort() {
local m="$1"
shift
CT_DoLog DEBUG "Testing '! ( $* )'"
test "$@" && CT_Abort "$m"
return 0
}
# Test a condition, and abort with an error message if not satisfied
# Usage: CT_TestAndAbort <message> <tests>
CT_TestOrAbort() {
local m="$1"
shift
CT_DoLog DEBUG "Testing '$*'"
test "$@" || CT_Abort "$m"
return 0
}
# Test the presence of a tool, or abort if not found
# Usage: CT_HasOrAbort <tool>
CT_HasOrAbort() {
CT_TestAndAbort "'${1}' not found and needed for successful toolchain build." -z "$(CT_Which "${1}")"
return 0
}
# Search a program: wrap "which" for those system where "which"
# verbosely says there is no match (such as on Mandriva).
# Usage: CT_Which <filename>
CT_Which() {
which "$1" 2>/dev/null || true
}
# Get current date with nanosecond precision
# On those system not supporting nanosecond precision, faked with rounding down
# to the highest entire second
# Usage: CT_DoDate <fmt>
CT_DoDate() {
date "$1" |${sed} -r -e 's/%?N$/000000000/;'
}
CT_STEP_COUNT=1
CT_STEP_MESSAGE[${CT_STEP_COUNT}]="(top-level)"
# Memorise a step being done so that any error is caught
# Usage: CT_DoStep <loglevel> <message>
CT_DoStep() {
local start=$(CT_DoDate +%s%N)
CT_DoLog "$1" "================================================================="
CT_DoLog "$1" "$2"
CT_STEP_COUNT=$((CT_STEP_COUNT+1))
CT_STEP_LEVEL[${CT_STEP_COUNT}]="$1"; shift
CT_STEP_START[${CT_STEP_COUNT}]="${start}"
CT_STEP_MESSAGE[${CT_STEP_COUNT}]="$1"
return 0
}
# End the step just being done
# Usage: CT_EndStep
CT_EndStep() {
local stop=$(CT_DoDate +%s%N)
local duration=$(printf "%032d" $((stop-${CT_STEP_START[${CT_STEP_COUNT}]})) \
|${sed} -r -e 's/([[:digit:]]{2})[[:digit:]]{7}$/\.\1/; s/^0+//; s/^\./0\./;'
)
local elapsed=$(printf "%02d:%02d" $((SECONDS/60)) $((SECONDS%60)))
local level="${CT_STEP_LEVEL[${CT_STEP_COUNT}]}"
local message="${CT_STEP_MESSAGE[${CT_STEP_COUNT}]}"
CT_STEP_COUNT=$((CT_STEP_COUNT-1))
CT_DoLog "${level}" "${message}: done in ${duration}s (at ${elapsed})"
return 0
}
# Pushes into a directory, and pops back
CT_Pushd() {
CT_DoLog DEBUG "Entering '$1'"
pushd "$1" >/dev/null 2>&1
}
CT_Popd() {
popd >/dev/null 2>&1
}
# Create a dir and cd or pushd into it
# Usage: CT_mkdir_cd <dir/to/create>
# CT_mkdir_pushd <dir/to/create>
CT_mkdir_cd() {
local dir="${1}"
mkdir -p "${dir}"
cd "${dir}"
}
CT_mkdir_pushd() {
local dir="${1}"
mkdir -p "${dir}"
CT_Pushd "${dir}"
}
# Creates a temporary directory
# $1: variable to assign to
# Usage: CT_MktempDir foo
CT_MktempDir() {
# Some mktemp do not allow more than 6 Xs
eval "$1"=$(mktemp -q -d "${CT_BUILD_DIR}/tmp.XXXXXX")
CT_TestOrAbort "Could not make temporary directory" -n "${!1}" -a -d "${!1}"
CT_DoLog DEBUG "Made temporary directory '${!1}'"
return 0
}
# Removes one or more directories, even if it is read-only, or its parent is
# Usage: CT_DoForceRmdir dir [...]
CT_DoForceRmdir() {
local dir
local mode
for dir in "${@}"; do
[ -d "${dir}" ] || continue
case "${CT_CONFIGURE_has_stat_flavor_GNU},${CT_CONFIGURE_has_stat_flavor_BSD}" in
y,*)
mode="$(stat -c '%a' "$(dirname "${dir}")")"
;;
*,y)
mode="$(stat -f '%Lp' "$(dirname "${dir}")")"
;;
*)
CT_Abort "Unknown stat format options"
;;
esac
CT_DoExecLog ALL chmod u+w "$(dirname "${dir}")"
CT_DoExecLog ALL chmod -R u+w "${dir}"
CT_DoExecLog ALL rm -rf "${dir}"
CT_DoExecLog ALL chmod ${mode} "$(dirname "${dir}")"
done
}
# Add the specified directory to LD_LIBRARY_PATH, and export it
# If the specified patch is already present, just export
# $1: path to add
# $2: add as 'first' or 'last' path, 'first' is assumed if $2 is empty
# Usage CT_SetLibPath /some/where/lib [first|last]
CT_SetLibPath() {
local path="$1"
local pos="$2"
case ":${LD_LIBRARY_PATH}:" in
*:"${path}":*) ;;
*) case "${pos}" in
last)
CT_DoLog DEBUG "Adding '${path}' at end of LD_LIBRARY_PATH"
LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}${path}"
;;
first|"")
CT_DoLog DEBUG "Adding '${path}' at start of LD_LIBRARY_PATH"
LD_LIBRARY_PATH="${path}${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
;;
*)
CT_Abort "Incorrect position '${pos}' to add '${path}' to LD_LIBRARY_PATH"
;;
esac
;;
esac
CT_DoLog DEBUG "==> LD_LIBRARY_PATH='${LD_LIBRARY_PATH}'"
export LD_LIBRARY_PATH
}
# Build up the list of allowed tarball extensions
# Add them in the prefered order; most preferred comes first
CT_DoListTarballExt()
{
printf ".tar.xz\n"
printf ".tar.lzma\n"
printf ".tar.bz2\n"
printf ".tar.gz\n.tgz\n"
printf ".tar\n"
printf ".zip\n"
}
# Get the file name extension of a component
# Usage: CT_GetFileExtension <component_name-component_version> [extension]
# If found, echoes the extension to stdout, and return 0
# If not found, echoes nothing on stdout, and return !0.
CT_GetFileExtension()
{
local ext
local file="$1"
shift
local first_ext="$1"
# we need to also check for an empty extension for those very
# peculiar components that don't have one (such as sstrip from
# buildroot).
for ext in ${first_ext} $(CT_DoListTarballExt); do
if [ -e "${CT_TARBALLS_DIR}/${file}${ext}" -o -L "${CT_TARBALLS_DIR}/${file}${ext}" ]; then
echo "${ext}"
exit 0
fi
done
exit 1
}
# Get file's basename by stripping supported archive extensions
CT_GetFileBasename()
{
local bn="${1}"
local ext
for ext in $(CT_DoListTarballExt); do
if [ "${bn%.${ext}}" != "${bn}" ]; then
echo "${bn%.${ext}}"
exit 0
fi
done
}
# Try to retrieve the specified URL (HTTP or FTP)
# Usage: CT_DoGetFile <URL>
# This functions always returns true (0), as it can be legitimate not
# to find the requested URL (think about snapshots, different layouts
# for different gcc versions, etc...).
CT_DoGetFile() {
local url="${1}"
local dest="${CT_TARBALLS_DIR}/${url##*/}"
local tmp="${dest}.tmp-dl"
local ok
local T
# Remove potential left-over from a previous run
rm -f "${tmp}"
# Replace a special value of '-1' with empty string
if [ ${CT_CONNECT_TIMEOUT} != -1 ]; then
T="${CT_CONNECT_TIMEOUT}"
fi
if [ "${CT_DOWNLOAD_AGENT_WGET}" = "y" ]; then
if CT_DoExecLog ALL wget ${CT_DOWNLOAD_WGET_OPTIONS} \
${T:+-T ${T}} \
-O "${tmp}" \
"${url}"; then
ok=y
fi
elif [ "${CT_DOWNLOAD_AGENT_CURL}" = "y" ]; then
if CT_DoExecLog ALL curl ${CT_DOWNLOAD_CURL_OPTIONS} \
${T:+--connect-timeout ${T}} \
-o "${tmp}" \
"${url}"; then
ok=y
fi
fi
if [ "${ok}" = "y" ]; then
# Success, we got it, good!
mv "${tmp}" "${dest}"
CT_DoLog DEBUG "Got it from: \"${url}\""
else
# Woops...
rm -f "${tmp}"
CT_DoLog DEBUG "Not at this location: \"${url}\""
fi
}
# This function tries to retrieve a tarball form a local directory
# Usage: CT_GetLocal <basename> [.extension]
CT_GetLocal() {
local basename="$1"
local first_ext="$2"
local ext
# Do we already have it in *our* tarballs dir?
if ext="$( CT_GetFileExtension "${basename}" ${first_ext} )"; then
CT_DoLog DEBUG "Already have '${basename}'"
return 0
fi
if [ -n "${CT_LOCAL_TARBALLS_DIR}" ]; then
CT_DoLog DEBUG "Trying to retrieve an already downloaded copy of '${basename}'"
# We'd rather have a bzip2'ed tarball, then gzipped tarball, plain tarball,
# or, as a failover, a file without extension.
for ext in ${first_ext} $(CT_DoListTarballExt) ''; do
CT_DoLog DEBUG "Trying '${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}'"
if [ -r "${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}" -a \
"${CT_FORCE_DOWNLOAD}" != "y" ]; then
CT_DoLog DEBUG "Got '${basename}' from local storage"
CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}" "${CT_TARBALLS_DIR}/${basename}${ext}"
return 0
fi
done
fi
return 1
}
# This function saves the specified to local storage if possible,
# and if so, symlinks it for later usage
# Usage: CT_SaveLocal </full/path/file.name>
CT_SaveLocal() {
local file="$1"
local basename="${file##*/}"
if [ "${CT_SAVE_TARBALLS}" = "y" ]; then
CT_DoLog EXTRA "Saving '${basename}' to local storage"
# The file may already exist if downloads are forced: remove it first
CT_DoExecLog ALL rm -f "${CT_LOCAL_TARBALLS_DIR}/${basename}"
CT_DoExecLog ALL mv -f "${file}" "${CT_LOCAL_TARBALLS_DIR}"
CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${basename}" "${file}"
fi
}
# Download the file from one of the URLs passed as argument
# Usage: CT_GetFile <basename> [.extension] <url> [url ...]
CT_GetFile() {
local ext
local -a URLS
local url
local file="$1"
local first_ext
shift
# If next argument starts with a dot, then this is not an URL,
# and we can consider that it is a preferred extension.
case "$1" in
.*) first_ext="$1"
shift
;;
esac
# Does it exist localy?
if CT_GetLocal "${file}" ${first_ext}; then
return 0
fi
# No, it does not...
# If not allowed to download from the Internet, don't
if [ "${CT_FORBID_DOWNLOAD}" = "y" ]; then
CT_DoLog DEBUG "Not allowed to download from the Internet, aborting ${file} download"
return 1
fi
# Try to retrieve the file
CT_DoLog EXTRA "Retrieving '${file}'"
Transform LAN_MIRROR into plain MIRROR: - it does not have to be in the LAN - offer mirror preference over upstream - if selected, the mirror will be scanned before upstream servers - if not selected, upstream servers will be scanned before the mirror - I've set up such an internet-accessible mirror - uClibc snapshot available - MPFR releases available (the MPFR site is down from time to time) - update all samples to use my mirror as a failover /trunk/scripts/functions | 45 16 29 0 +++++-------- /trunk/samples/x86_64-unknown-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/sh4-unknown-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/armeb-unknown-linux-uclibc/crosstool.config | 8 7 1 0 ++ /trunk/samples/arm-unknown-elf/crosstool.config | 8 7 1 0 ++ /trunk/samples/arm-unknown-linux-gnueabi/crosstool.config | 8 7 1 0 ++ /trunk/samples/ia64-unknown-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/x86_64-unknown-linux-uclibc/crosstool.config | 8 7 1 0 ++ /trunk/samples/armeb-unknown-linux-gnueabi/crosstool.config | 8 7 1 0 ++ /trunk/samples/powerpc-e500v2-linux-gnuspe/crosstool.config | 8 7 1 0 ++ /trunk/samples/i686-nptl-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/powerpc-unknown-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/arm-unknown-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/mips-unknown-linux-uclibc/crosstool.config | 8 7 1 0 ++ /trunk/samples/arm-unknown-linux-uclibcgnueabi/crosstool.config | 8 7 1 0 ++ /trunk/samples/armeb-unknown-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/powerpc-unknown_nofpu-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/powerpc-unknown-linux-uclibc/crosstool.config | 8 7 1 0 ++ /trunk/samples/arm-unknown-linux-uclibc/crosstool.config | 8 7 1 0 ++ /trunk/samples/i586-geode-linux-uclibc/crosstool.config | 8 7 1 0 ++ /trunk/samples/powerpc-405-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/armeb-unknown-linux-uclibcgnueabi/crosstool.config | 8 7 1 0 ++ /trunk/samples/mipsel-unknown-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/arm-unknown-eabi/crosstool.config | 8 7 1 0 ++ /trunk/samples/alphaev56-unknown-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/powerpc-860-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/config/global/download.in | 42 25 17 0 +++++++----- 27 files changed, 216 insertions(+), 71 deletions(-)
2008-10-31 18:27:27 +00:00
# Add URLs on the LAN mirror
if [ "${CT_USE_MIRROR}" = "y" ]; then
CT_TestOrAbort "Please set the mirror base URL" -n "${CT_MIRROR_BASE_URL}"
URLS+=( "${CT_MIRROR_BASE_URL}/${file%-*}" )
URLS+=( "${CT_MIRROR_BASE_URL}" )
fi
if [ "${CT_FORCE_MIRROR}" != "y" ]; then
URLS+=( "${@}" )
fi
Transform LAN_MIRROR into plain MIRROR: - it does not have to be in the LAN - offer mirror preference over upstream - if selected, the mirror will be scanned before upstream servers - if not selected, upstream servers will be scanned before the mirror - I've set up such an internet-accessible mirror - uClibc snapshot available - MPFR releases available (the MPFR site is down from time to time) - update all samples to use my mirror as a failover /trunk/scripts/functions | 45 16 29 0 +++++-------- /trunk/samples/x86_64-unknown-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/sh4-unknown-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/armeb-unknown-linux-uclibc/crosstool.config | 8 7 1 0 ++ /trunk/samples/arm-unknown-elf/crosstool.config | 8 7 1 0 ++ /trunk/samples/arm-unknown-linux-gnueabi/crosstool.config | 8 7 1 0 ++ /trunk/samples/ia64-unknown-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/x86_64-unknown-linux-uclibc/crosstool.config | 8 7 1 0 ++ /trunk/samples/armeb-unknown-linux-gnueabi/crosstool.config | 8 7 1 0 ++ /trunk/samples/powerpc-e500v2-linux-gnuspe/crosstool.config | 8 7 1 0 ++ /trunk/samples/i686-nptl-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/powerpc-unknown-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/arm-unknown-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/mips-unknown-linux-uclibc/crosstool.config | 8 7 1 0 ++ /trunk/samples/arm-unknown-linux-uclibcgnueabi/crosstool.config | 8 7 1 0 ++ /trunk/samples/armeb-unknown-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/powerpc-unknown_nofpu-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/powerpc-unknown-linux-uclibc/crosstool.config | 8 7 1 0 ++ /trunk/samples/arm-unknown-linux-uclibc/crosstool.config | 8 7 1 0 ++ /trunk/samples/i586-geode-linux-uclibc/crosstool.config | 8 7 1 0 ++ /trunk/samples/powerpc-405-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/armeb-unknown-linux-uclibcgnueabi/crosstool.config | 8 7 1 0 ++ /trunk/samples/mipsel-unknown-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/arm-unknown-eabi/crosstool.config | 8 7 1 0 ++ /trunk/samples/alphaev56-unknown-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/samples/powerpc-860-linux-gnu/crosstool.config | 8 7 1 0 ++ /trunk/config/global/download.in | 42 25 17 0 +++++++----- 27 files changed, 216 insertions(+), 71 deletions(-)
2008-10-31 18:27:27 +00:00
# Scan all URLs in turn, and try to grab a tarball from there
# Do *not* try git trees (ext=/.git), this is handled in a specific
# wrapper, below
for ext in ${first_ext} $(CT_DoListTarballExt) ''; do
# Try all urls in turn
for url in "${URLS[@]}"; do
[ -n "${url}" ] || continue
CT_DoLog DEBUG "Trying '${url}/${file}${ext}'"
CT_DoGetFile "${url}/${file}${ext}"
if [ -f "${CT_TARBALLS_DIR}/${file}${ext}" ]; then
CT_DoLog DEBUG "Got '${file}' from the Internet"
CT_SaveLocal "${CT_TARBALLS_DIR}/${file}${ext}"
return 0
fi
done
done
# Just return error: CT_DoFetch will check it and will handle it appropriately.
return 1
}
# TBD these should not be needed if config.sub/guess is a package
# Two wrappers to call config.(guess|sub) either from CT_TOP_DIR or CT_LIB_DIR.
# Those from CT_TOP_DIR, if they exist, will be be more recent than those from CT_LIB_DIR.
CT_DoConfigGuess() {
if [ -x "${CT_TOP_DIR}/scripts/config.guess" ]; then
"${CT_TOP_DIR}/scripts/config.guess"
else
"${CT_LIB_DIR}/scripts/config.guess"
fi
}
CT_DoConfigSub() {
if [ -x "${CT_TOP_DIR}/scripts/config.sub" ]; then
"${CT_TOP_DIR}/scripts/config.sub" "$@"
else
"${CT_LIB_DIR}/scripts/config.sub" "$@"
fi
}
multilib: Determine which options may pass through. On some arches (e.g. MIPS) the options like -mabi do not work if specified more than once (see the comment in 100-gcc.sh). Therefore, we need to determine which of the options produced by <arch>.sh can be passed to multilib builds and which must be removed (i.e., which options vary among the multilibs). This presents a chicken-and-egg problem. GCC developers, in their infinite wisdom, do not allow arbitrary multilib specification to be supplied to GCC's configure. Instead, the target (and sometimes some extra options) determine the set of multilibs - which may include different CPUs, different ABIs, different endianness, different FPUs, different floating-point ABIs, ... That is, we don't know which parts vary until we build GCC and ask it. So, the solution implemented here is: - For multilib builds, start with empty CT_ARCH_TARGET_CFLAGS/LDFLAGS. - For multilib builds, require core pass 1. Pass 1 does not build any target binaries, so at that point, our target options have not been used yet. - Provide an API to modify the environment variables for the steps that follow the current one. - As a part of multilib-related housekeeping, determine the variable part of multilibs and filter out these options; pass the rest into CT_TARGET_CFLAGS/LDFLAGS. This still does not handle extra dependencies between GCC options (like -ma implying -mcpu=X -mtune=Y, etc.) but I feel that would complicate matters too much. Let's leave this until there's a compelling case for it. Also, query GCC's sysroot suffix for targets that use it (SuperH, for example) - the default multilib may not work if the command line specifies the default option explicitly (%sysroot_suffix_spec is not aware of multilib defaults). Signed-off-by: Alexey Neyman <stilor@att.net>
2016-03-30 19:15:54 +00:00
# Normally, each step is executed in a sub-shell and thus cannot modify the
# environment for the next step(s). When this is needed, it can do so by
# invoking this function.
# Usage: CT_EnvModify VAR VALUE
CT_EnvModify() {
echo "${1}=\"${2}\"" >> "${CT_BUILD_DIR}/env.modify.sh"
}
# Compute the target tuple from what is provided by the user
# Usage: CT_DoBuildTargetTuple
# In fact this function takes the environment variables to build the target
# tuple. It is needed both by the normal build sequence, as well as the
# sample saving sequence.
CT_DoBuildTargetTuple() {
# Set the endianness suffix, and the default endianness gcc option
case "${CT_ARCH_ENDIAN}" in
big)
target_endian_eb=eb
target_endian_be=be
target_endian_el=
target_endian_le=
CT_ARCH_ENDIAN_CFLAG="-mbig-endian"
CT_ARCH_ENDIAN_LDFLAG="-Wl,-EB"
;;
little)
target_endian_eb=
target_endian_be=
target_endian_el=el
target_endian_le=le
CT_ARCH_ENDIAN_CFLAG="-mlittle-endian"
CT_ARCH_ENDIAN_LDFLAG="-Wl,-EL"
;;
esac
# Set the bitness suffix
case "${CT_ARCH_BITNESS}" in
32)
target_bits_32=32
target_bits_64=
;;
64)
target_bits_32=
target_bits_64=64
;;
esac
# Build the default architecture tuple part
CT_TARGET_ARCH="${CT_ARCH}${CT_ARCH_SUFFIX}"
# Set defaults for the system part of the tuple. Can be overriden
# by architecture-specific values.
case "${CT_LIBC}" in
*glibc) CT_TARGET_SYS=gnu;;
uClibc) CT_TARGET_SYS=uclibc;;
musl) CT_TARGET_SYS=musl;;
bionic) CT_TARGET_SYS=android;;
avr-libc)
# avr-libc only seems to work with the non-canonical "avr" target.
CT_TARGET_SKIP_CONFIG_SUB=y
CT_TARGET_SYS= # CT_TARGET_SYS must be empty too
;;
*) CT_TARGET_SYS=elf;;
esac
# Set the default values for ARCH, ABI, CPU, TUNE, FPU and FLOAT
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
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
[ "${CT_ARCH_ARCH}" ] && { CT_ARCH_ARCH_CFLAG="-march=${CT_ARCH_ARCH}"; CT_ARCH_WITH_ARCH="--with-arch=${CT_ARCH_ARCH}"; }
[ "${CT_ARCH_ABI}" ] && { CT_ARCH_ABI_CFLAG="-mabi=${CT_ARCH_ABI}"; CT_ARCH_WITH_ABI="--with-abi=${CT_ARCH_ABI}"; }
[ "${CT_ARCH_CPU}" ] && { CT_ARCH_CPU_CFLAG="-mcpu=${CT_ARCH_CPU}"; CT_ARCH_WITH_CPU="--with-cpu=${CT_ARCH_CPU}"; }
[ "${CT_ARCH_TUNE}" ] && { CT_ARCH_TUNE_CFLAG="-mtune=${CT_ARCH_TUNE}"; CT_ARCH_WITH_TUNE="--with-tune=${CT_ARCH_TUNE}"; }
[ "${CT_ARCH_FPU}" ] && { CT_ARCH_FPU_CFLAG="-mfpu=${CT_ARCH_FPU}"; CT_ARCH_WITH_FPU="--with-fpu=${CT_ARCH_FPU}"; }
case "${CT_ARCH_FLOAT}" in
hard)
CT_ARCH_FLOAT_CFLAG="-mhard-float"
CT_ARCH_WITH_FLOAT="--with-float=hard"
;;
soft)
CT_ARCH_FLOAT_CFLAG="-msoft-float"
CT_ARCH_WITH_FLOAT="--with-float=soft"
;;
softfp)
CT_ARCH_FLOAT_CFLAG="-mfloat-abi=softfp"
CT_ARCH_WITH_FLOAT="--with-float=softfp"
;;
esac
# Build the default kernel tuple part
CT_TARGET_KERNEL="${CT_KERNEL}"
# Overide the default values with the components specific settings
CT_DoArchTupleValues
CT_DoKernelTupleValues
# Finish the target tuple construction
CT_TARGET="${CT_TARGET_ARCH}"
CT_TARGET="${CT_TARGET}${CT_TARGET_VENDOR:+-${CT_TARGET_VENDOR}}"
CT_TARGET="${CT_TARGET}${CT_TARGET_KERNEL:+-${CT_TARGET_KERNEL}}"
CT_TARGET="${CT_TARGET}${CT_TARGET_SYS:+-${CT_TARGET_SYS}}"
# Sanity checks
__sed_alias=""
if [ -n "${CT_TARGET_ALIAS_SED_EXPR}" ]; then
__sed_alias=$(echo "${CT_TARGET}" |${sed} -r -e "${CT_TARGET_ALIAS_SED_EXPR}")
fi
case ":${CT_TARGET_VENDOR}:${CT_TARGET_ALIAS}:${__sed_alias}:" in
:*" "*:*:*:) CT_Abort "Don't use spaces in the vendor string, it breaks things.";;
:*"-"*:*:*:) CT_Abort "Don't use dashes in the vendor string, it breaks things.";;
:*:*" "*:*:) CT_Abort "Don't use spaces in the target alias, it breaks things.";;
:*:*:*" "*:) CT_Abort "Don't use spaces in the target sed transform, it breaks things.";;
esac
# Canonicalise it
if [ "${CT_TARGET_SKIP_CONFIG_SUB}" != "y" ]; then
CT_TARGET=$(CT_DoConfigSub "${CT_TARGET}")
fi
# Prepare the target CFLAGS
CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ENDIAN_CFLAG}"
CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ARCH_CFLAG}"
CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_ABI_CFLAG}"
CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_CPU_CFLAG}"
CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_TUNE_CFLAG}"
CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FPU_CFLAG}"
CT_ARCH_TARGET_CFLAGS="${CT_ARCH_TARGET_CFLAGS} ${CT_ARCH_FLOAT_CFLAG}"
# Now on for the target LDFLAGS
CT_ARCH_TARGET_LDFLAGS="${CT_ARCH_TARGET_LDFLAGS} ${CT_ARCH_ENDIAN_LDFLAG}"
multilib: Determine which options may pass through. On some arches (e.g. MIPS) the options like -mabi do not work if specified more than once (see the comment in 100-gcc.sh). Therefore, we need to determine which of the options produced by <arch>.sh can be passed to multilib builds and which must be removed (i.e., which options vary among the multilibs). This presents a chicken-and-egg problem. GCC developers, in their infinite wisdom, do not allow arbitrary multilib specification to be supplied to GCC's configure. Instead, the target (and sometimes some extra options) determine the set of multilibs - which may include different CPUs, different ABIs, different endianness, different FPUs, different floating-point ABIs, ... That is, we don't know which parts vary until we build GCC and ask it. So, the solution implemented here is: - For multilib builds, start with empty CT_ARCH_TARGET_CFLAGS/LDFLAGS. - For multilib builds, require core pass 1. Pass 1 does not build any target binaries, so at that point, our target options have not been used yet. - Provide an API to modify the environment variables for the steps that follow the current one. - As a part of multilib-related housekeeping, determine the variable part of multilibs and filter out these options; pass the rest into CT_TARGET_CFLAGS/LDFLAGS. This still does not handle extra dependencies between GCC options (like -ma implying -mcpu=X -mtune=Y, etc.) but I feel that would complicate matters too much. Let's leave this until there's a compelling case for it. Also, query GCC's sysroot suffix for targets that use it (SuperH, for example) - the default multilib may not work if the command line specifies the default option explicitly (%sysroot_suffix_spec is not aware of multilib defaults). Signed-off-by: Alexey Neyman <stilor@att.net>
2016-03-30 19:15:54 +00:00
# Now, a multilib quirk. We may not be able to pass CT_ARCH_TARGET_CFLAGS
# and CT_ARCH_TARGET_LDFLAGS to gcc: even though GCC build appends the multilib
# flags afterwards, on some architectures the build breaks because some
# flags do not completely override each other. For example, on mips target,
# 'gcc -mabi=32' and 'gcc -mabi=n32' both work, but 'gcc -mabi=32 -mabi=n32'
# triggers an internal linker error. Likely a bug in GNU binutils, but we
# have to work it around for now: *do not pass the CT_ARCH_TARGET_ flags*.
# Instead, save them into a different variable here. Then, after the first
# core pass, we'll know which of them vary with multilibs (i.e. must be
# filtered out).
if [ "${CT_MULTILIB}" = "y" ]; then
CT_ARCH_TARGET_CFLAGS_MULTILIB="${CT_ARCH_TARGET_CFLAGS}"
CT_ARCH_TARGET_CFLAGS=
CT_ARCH_TARGET_LDFLAGS_MULTILIB="${CT_ARCH_TARGET_LDFLAGS}"
CT_ARCH_TARGET_LDFLAGS=
fi
}
# This function does pause the build until the user strikes "Return"
# Usage: CT_DoPause [optional_message]
CT_DoPause() {
local foo
local message="${1:-Pausing for your pleasure}"
CT_DoLog INFO "${message}"
read -p "Press 'Enter' to continue, or Ctrl-C to stop..." foo >&6
return 0
}
# This function creates a tarball of the specified directory, but
# only if it exists
# Usage: CT_DoTarballIfExists <dir> <tarball_basename> [extra_tar_options [...]]
CT_DoTarballIfExists() {
local dir="$1"
local tarball="$2"
shift 2
local -a extra_tar_opts=( "$@" )
local -a compress
case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
y) compress=( gzip -c -3 - ); tar_ext=.gz;;
*) compress=( cat - ); tar_ext=;;
esac
if [ -d "${dir}" ]; then
CT_DoLog DEBUG " Saving '${dir}'"
{ tar c -C "${dir}" -v -f - "${extra_tar_opts[@]}" . \
|"${compress[@]}" >"${tarball}.tar${tar_ext}" ;
} 2>&1 |${sed} -r -e 's/^/ /;' |CT_DoLog STATE
else
CT_DoLog STATE " Not saving '${dir}': does not exist"
fi
}
# This function extracts a tarball to the specified directory, but
# only if the tarball exists
# Usage: CT_DoExtractTarballIfExists <tarball_basename> <dir> [extra_tar_options [...]]
CT_DoExtractTarballIfExists() {
local tarball="$1"
local dir="$2"
shift 2
local -a extra_tar_opts=( "$@" )
local -a uncompress
case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
y) uncompress=( gzip -c -d ); tar_ext=.gz;;
*) uncompress=( cat ); tar_ext=;;
esac
if [ -f "${tarball}.tar${tar_ext}" ]; then
CT_DoLog DEBUG " Restoring '${dir}'"
CT_DoForceRmdir "${dir}"
CT_DoExecLog DEBUG mkdir -p "${dir}"
{ "${uncompress[@]}" "${tarball}.tar${tar_ext}" \
|tar x -C "${dir}" -v -f - "${extra_tar_opts[@]}" ;
} 2>&1 |${sed} -r -e 's/^/ /;' |CT_DoLog STATE
else
CT_DoLog STATE " Not restoring '${dir}': does not exist"
fi
}
# This function saves the state of the toolchain to be able to restart
# at any one point
# Usage: CT_DoSaveState <next_step_name>
CT_DoSaveState() {
[ "${CT_DEBUG_CT_SAVE_STEPS}" = "y" ] || return 0
local state_name="$1"
local state_dir="${CT_STATE_DIR}/${state_name}"
CT_DoLog INFO "Saving state to restart at step '${state_name}'..."
rm -rf "${state_dir}"
mkdir -p "${state_dir}"
CT_DoLog STATE " Saving environment and aliases"
# We must omit shell functions, and some specific bash variables
# that break when restoring the environment, later. We could do
# all the processing in the awk script, but a sed is easier...
set |${awk} '
BEGIN { _p = 1; }
$0~/^[^ ]+ \(\)/ { _p = 0; }
_p == 1
$0 == "}" { _p = 1; }
' |${sed} -r -e '/^BASH_(ARGC|ARGV|LINENO|SOURCE|VERSINFO)=/d;
/^(UID|EUID)=/d;
/^(FUNCNAME|GROUPS|PPID|SHELLOPTS)=/d;' >"${state_dir}/env.sh"
CT_DoTarballIfExists "${CT_BUILDTOOLS_PREFIX_DIR}" "${state_dir}/buildtools_dir"
CT_DoTarballIfExists "${CT_PREFIX_DIR}" "${state_dir}/prefix_dir" --exclude '*.log'
CT_DoLog STATE " Saving log file"
CT_LogDisable
case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
y) gzip -3 -c "${CT_BUILD_LOG}" >"${state_dir}/log.gz";;
*) cat "${CT_BUILD_LOG}" >"${state_dir}/log";;
esac
CT_LogEnable
}
# This function restores a previously saved state
# Usage: CT_DoLoadState <state_name>
CT_DoLoadState(){
local state_name="$1"
local state_dir="${CT_STATE_DIR}/${state_name}"
local old_RESTART="${CT_RESTART}"
local old_STOP="${CT_STOP}"
CT_TestOrAbort "The previous build did not reach the point where it could be restarted at '${CT_RESTART}'" -d "${state_dir}"
CT_DoLog INFO "Restoring state at step '${state_name}', as requested."
CT_DoExtractTarballIfExists "${state_dir}/prefix_dir" "${CT_PREFIX_DIR}"
CT_DoExtractTarballIfExists "${state_dir}/buildtools_dir" "${CT_BUILDTOOLS_PREFIX_DIR}"
# Restore the environment, discarding any error message
# (for example, read-only bash internals)
CT_DoLog STATE " Restoring environment"
. "${state_dir}/env.sh" >/dev/null 2>&1 || true
# Restore the new RESTART and STOP steps
CT_RESTART="${old_RESTART}"
CT_STOP="${old_STOP}"
unset old_stop old_restart
CT_DoLog STATE " Restoring log file"
CT_LogDisable
mv "${CT_BUILD_LOG}" "${CT_BUILD_LOG}.tail"
case "${CT_DEBUG_CT_SAVE_STEPS_GZIP}" in
y) gzip -dc "${state_dir}/log.gz" >"${CT_BUILD_LOG}";;
*) cat "${state_dir}/log" >"${CT_BUILD_LOG}";;
esac
cat "${CT_BUILD_LOG}.tail" >>"${CT_BUILD_LOG}"
CT_LogEnable
rm -f "${CT_BUILD_LOG}.tail"
}
# This function sets a kconfig option to a specific value in a .config file
# Usage: CT_KconfigSetOption <option> <value> <file>
CT_KconfigSetOption() {
local option="$1"
local value="$2"
local file="$3"
${grep} -E -q "^${option}=.*" "${file}" && \
${sed} -i -r -e "s;^${option}=.*$;${option}=${value};" "${file}" || \
${grep} -E -q "^# ${option} is not set$" "${file}" && \
${sed} -i -r -e "s;^# ${option} is not set$;${option}=${value};" "${file}" || \
echo "${option}=${value}" >> "${file}"
}
# This function enables a kconfig option to '=y' in a .config file
# Usage: CT_KconfigEnableOption <option> <file>
CT_KconfigEnableOption() {
local option="$1"
local file="$2"
CT_KconfigSetOption "${option}" "y" "${file}"
}
# This function disables a kconfig option in a .config file
# Usage: CT_KconfigDisableOption <option> <file>
CT_KconfigDisableOption() {
local option="${1}"
local file="${2}"
${grep} -E -q "^# ${option} is not set$" "${file}" || \
${grep} -E -q "^${option}=.*$" "${file}" && \
${sed} -i -r -e "s;^${option}=.*$;# ${option} is not set;" "${file}" || \
echo "# ${option} is not set" >> "${file}"
}
# This function deletes a kconfig option in a .config file, no matter if it
# is set or commented out.
# Usage: CT_KconfigDeleteOption <option> <file>
CT_KconfigDeleteOption() {
local option="${1}"
local file="${2}"
${grep} -E -q "^# ${option} is not set$" "${file}" && \
${sed} -i -r -e "/^# ${option} is not set$/d" "${file}" || \
${grep} -E -q "^${option}=.*$" "${file}" && \
${sed} -i -r -e "/^${option}=.*$/d" "${file}" || true
}
# Multilib iterator. The caller should be in a directory where the directories
# will be created, one per multilib, and the specified command will be run in
# each of them. The following arguments will be passed to the invoked command:
# multi_flags CFLAGS for this multilib
# multi_dir GCC internal library location for the multilib
# multi_os_dir OS library location for the multilib
# multi_os_dir_gcc Same as multi_os_dir, preserved from GCC output
# multi_root Sysroot for this multilib
# multi_target Target tuple, either as reported by GCC or by our guesswork
# multi_count Total number of multilibs
# multi_index Index of the current multilib
# Any additional arguments passed to this function will be forwarded to the called
# function as well.
# Usage: CT_IterateMultilibs <function> <prefix> <additional-args...>
CT_IterateMultilibs() {
local func="${1}"
local prefix="${2}"
local -a multilibs
local multi_dir multi_os_dir multi_os_dir_gcc multi_root multi_flags multi_index multi_target
local root_suffix
local dir_postfix
# Name used internally below
if [ "${prefix}" = "sysroot-check" ]; then
CT_Abort "Bad prefix used in CT_IterateMultilibs"
fi
# Drop mandatory arguments
shift 2
# If gcc is not configured for multilib, it still prints a single line
# for the default settings
multilibs=( $("${CT_TARGET}-${CT_CC}" -print-multi-lib 2>/dev/null) )
CT_DoExecLog ALL rm -rf "sysroot-check"
for multilib in "${multilibs[@]}"; do
# GCC makes the distinction between:
# multilib (-print-multi-lib or -print-multi-directory) and
# multilib-os (--print-multi-os-directory)
# as the gcc library and gcc sysroot library paths, respectively.
# For example, on x86_64:
# multilib: -m32=32 -m64=.
# multilib-os: -m32=../lib -m64=../lib64
# Moreover, while some multilibs can coexist in the same sysroot (e.g.
# on x86), some have a "sysroot suffix" to separate incompatible variants.
# Such sysroot suffixes combine with multilib-os directories, e.g.
# on sh4 with -m4a multilib, the search order in sysroot is (dropping some
# directories for brevity:
# <sysroot>/m4a/lib/m4a/
# <sysroot>/m4a/usr/lib/m4a/
# <sysroot>/m4a/lib/
# <sysroot>/m4a/usr/lib/
# The problem is that while GCC itself is aware of these subtleties, the
# binutils (notably, ld) it invokes under the hood are not. For example,
# if a shared library libfoo.so.1 requires libbar.so.1, ld will only search
# for libbar.so.1 in <sysroot>/m4a/usr/lib, but not in <sysroot>/m4a/usr/lib/m4a.
# In other words, 'gcc -lfoo -lbar' will work for both the default and -m4a
# cases, and 'gcc -lfoo' will work for the default, but not for -m4a. To
# address this, we first try to determine if the sysroot alone makes the
# configuration sufficiently unique. If there are no multilibs within the
# same suffixed sysroot, we can drop the multi_os_dir and both gcc and ld
# will work. If not, we'll supply both multi_root/multi_os_dir (which will
# likely break later, e.g. while building final GCC with C++ support). But,
# we've done all we can.
# We do supply original multi_os_dir for consumers that need to look inside
# GCC's directories (e.g. to locate the libraries), under the name of
# multi_os_dir_gcc.
multi_flags=$( echo "${multilib#*;}" | ${sed} -r -e 's/@/ -/g;' )
multi_dir="${multilib%%;*}"
multi_os_dir=$( "${CT_TARGET}-${CT_CC}" -print-multi-os-directory ${multi_flags} )
multi_root=$( "${CT_TARGET}-${CT_CC}" -print-sysroot ${multi_flags} )
root_suffix="${multi_root#${CT_SYSROOT_DIR}}"
CT_DoExecLog ALL mkdir -p "sysroot-check${root_suffix}"
if [ -e "sysroot-check${root_suffix}/seen" ]; then
CT_DoExecLog ALL rm -f "sysroot-check${root_suffix}/unique"
else
CT_DoExecLog ALL touch "sysroot-check${root_suffix}/seen" \
"sysroot-check${root_suffix}/unique"
fi
done
# Now, actual iteration.
# This uses either GCC's multiarch feature (if supported; if not,
# GCC prints nothing and exits with status 0), falling back to calling
# the architecture-specific functions.
multi_index=1
for multilib in "${multilibs[@]}"; do
multi_flags=$( echo "${multilib#*;}" | ${sed} -r -e 's/@/ -/g;' )
multi_dir="${multilib%%;*}"
multi_os_dir=$( "${CT_TARGET}-${CT_CC}" -print-multi-os-directory ${multi_flags} )
multi_os_dir_gcc="${multi_os_dir}"
multi_root=$( "${CT_TARGET}-${CT_CC}" -print-sysroot ${multi_flags} )
multi_target=$( "${CT_TARGET}-${CT_CC}" -print-multiarch ${multi_flags} )
root_suffix="${multi_root#${CT_SYSROOT_DIR}}"
# If GCC did not report the target tuple (i.e. this configuration is not
# multiarch-capable), fall back to our guesswork.
if [ -z "${multi_target}" ]; then
multi_target="${CT_TARGET}"
CT_DoArchMultilibTarget multi_target ${multi_flags}
fi
# Avoid multi_os_dir if it's the only directory in this sysroot.
if [ -e "sysroot-check${root_suffix}/unique" ]; then
multi_os_dir=.
fi
# Brain-dead Cygwin hack: Cygwin cannot run a binary if there is a directory
# component in the path that ends with a dot. Unfortunately, that's the case
# for the default library name with GCC.
dir_postfix=_${multi_dir//\//_}
dir_postfix=${dir_postfix%_.}
CT_mkdir_pushd "${prefix}${dir_postfix}"
$func multi_dir="${multi_dir}" \
multi_os_dir="${multi_os_dir}" \
multi_os_dir_gcc="${multi_os_dir_gcc}" \
multi_flags="${multi_flags}" \
multi_root="${multi_root}" \
multi_target="${multi_target}" \
multi_index="${multi_index}" \
multi_count="${#multilibs[@]}" \
"$@"
CT_Popd
multi_index=$((multi_index+1))
done
}
# Create symbolic links in buildtools for binutils using a different
# target name.
# Usage:
# CT_SymlinkTools BIN-DIR SRC-DIR NEW-PREFIX SED-EXPR
CT_SymlinkTools()
{
local bindir="$1"
local srcdir="$2"
local newpfx="$3"
local sedexpr="$4"
local dirpfx
local t _t
# if bindir==srcdir, create symlinks just with the filename
if [ "${bindir}" != "${srcdir}" ]; then
dirpfx="${srcdir}/"
fi
CT_Pushd "${srcdir}"
for t in "${CT_TARGET}-"*; do
if [ "${t}" = "${CT_TARGET}-*" ]; then
# No matching files
break
fi
if [ -n "${newpfx}" -a \( "${newpfx}" != "${CT_TARGET}" -o "${bindir}" != "${srcdir}" \) ]; then
_t="${newpfx}-${t#${CT_TARGET}-}"
CT_DoExecLog ALL ln -sfv "${dirpfx}${t}" "${bindir}/${_t}"
fi
if [ -n "${sedexpr}" ]; then
_t=$( echo "${t}" | sed -r -e "${sedexpr}" )
if [ "${_t}" = "${t}" ]; then
CT_DoLog WARN "The sed expression '${sedexpr}' has no effect on '${t}'"
else
CT_DoExecLog ALL ln -sfv "${dirpfx}${t}" "${bindir}/${_t}"
fi
fi
done
CT_Popd
}
# Create symbolic links for multilib iterator. Expects ${multi_target}
# variable to indicate the desired triplet for the tools.
CT_SymlinkToolsMultilib()
{
# Make configure detect ${target}-tool binaries even if it is different
# from configured tuple. Only symlink to final tools if they're executable
# on build.
CT_SymlinkTools "${CT_BUILDTOOLS_PREFIX_DIR}/bin" \
"${CT_BUILDTOOLS_PREFIX_DIR}/bin" "${multi_target}"
case "${CT_TOOLCHAIN_TYPE}" in
native|cross)
CT_SymlinkTools "${CT_BUILDTOOLS_PREFIX_DIR}/bin" \
"${CT_PREFIX_DIR}/bin" "${multi_target}"
;;
esac
}
# Helper (iterator) for CT_MultilibFixupLDSO
CT__FixupLDSO()
{
local multi_dir multi_os_dir multi_root multi_flags multi_index multi_count multi_target
local binary
local ldso ldso_l ldso_f ldso_d ldso_u multilib_dir
for arg in "$@"; do
eval "${arg// /\\ }"
done
CT_DoLog EXTRA "Checking dynamic linker for multilib '${multi_flags}'"
multilib_dir="/lib/${multi_os_dir}"
CT_SanitizeVarDir multilib_dir
CT_DoExecLog ALL "${CT_TARGET}-${CT_CC}" -o test-ldso ../test-ldso.c ${multi_flags}
if [ -r "test-ldso.gdb" ]; then
binary="test-ldso.gdb"
else
binary="test-ldso"
fi
if ${CT_TARGET}-readelf -Wl "${binary}" | grep -q 'Requesting program interpreter: '; then
ldso=$( ${CT_TARGET}-readelf -Wl "${binary}" | \
grep 'Requesting program interpreter: ' | \
sed -e 's,.*: ,,' -e 's,\].*,,' )
fi
CT_DoLog DEBUG "Detected dynamic linker for multilib '${multi_flags}': '${ldso}'"
# Create symlink if GCC produced a dynamically linked executable.
if [ -z "${ldso}" ]; then
return # Probably, we're building a static toolchain.
fi
ldso_d="${ldso%/ld*.so.*}"
ldso_f="${ldso##*/}"
# Convert ldso_d to "how many levels we need to go up" and remove
# leading slash.
ldso_u=$( echo "${ldso_d#/}" | sed 's,[^/]\+,..,g' )
# If the requested dynamic linker exists, but is a symlink - check that it is either
# relative (in which case, if it is readable, we trust libc to have created it properly)
# or otherwise, convert it from absolute (target) path to a relative path that works on
# both host & target.
if [ -L "${multi_root}${ldso}" ]; then
ldso_l=`readlink "${multi_root}${ldso}"`
case "${ldso_l}" in
/*) # Absolute, convert to relative
if [ -r "${multi_root}${ldso_l}" ]; then
CT_DoExecLog ALL ln -sfv "${ldso_u}${ldso_l}" "${multi_root}${ldso}"
else
CT_DoLog WARN "Compiler selects '${ldso}' as dynamic linker for '${multi_flags}'"
CT_DoLog WARN "but '${ldso}' is a symlink to '${ldso_l}' which is not valid on target."
fi
;;
*) # Relative, must be readable
if [ ! -r "${multi_root}${ldso}" ]; then
CT_DoLog WARN "Compiler selects '${ldso}' as dynamic linker for '${multi_flags}'"
CT_DoLog WARN "but '${ldso}' is a symlink to '${ldso_l}' which is invalid relative symlink."
fi
;;
esac
return
elif [ -r "${multi_root}${ldso}" ]; then
return # Not a symlink but readable - looks like libc installed a real executable.
fi
# Is it requesting a linker not in the current directory? uClibc case.
if [ "${ldso_d}" != "${multilib_dir}" ]; then
CT_DoExecLog ALL ln -sfv "${ldso_u}${multilib_dir}/${ldso_f}" \
"${multi_root}${ldso}"
fi
}
# Go over multilib variants and check that the requested dynamic linker
# is present and resolves on both target and host.
CT_MultilibFixupLDSO()
{
CT_DoStep INFO "Checking dynamic linker symlinks"
CT_mkdir_pushd "${CT_BUILD_DIR}/build-libc-check-ldso"
echo "int main(void) { return 0; }" > test-ldso.c
CT_IterateMultilibs CT__FixupLDSO ldso_fixup
CT_Popd
CT_EndStep
}
# List the download mirrors. Usage:
# CT_Mirrors ORGANIZATION PROJECT [...]
CT_Mirrors()
{
local org="${1}"
local project="${2}"
case "${org}" in
GNU)
echo "https://ftpmirror.gnu.org/gnu/${project}"
echo "http://ftpmirror.gnu.org/gnu/${project}"
echo "https://ftp.gnu.org/gnu/${project}"
echo "http://ftp.gnu.org/gnu/${project}"
echo "ftp://ftp.gnu.org/gnu/${project}"
;;
sourceware)
local subdir="${3:+/${3}}"
echo "ftp://sourceware.org/pub/${project}/releases${subdir}"
echo "http://mirrors.kernel.org/sourceware/${project}/releases${subdir}"
echo "http://gcc.gnu.org/pub/${project}/releases${subdir}"
;;
Linaro)
eval "local version=\"\${${3}}\""
local base yymm
base="${version%%-*}"
yymm="${version##*-??}"
echo "https://releases.linaro.org/components/toolchain/${project}-linaro/${version}"
echo "https://releases.linaro.org/archive/${yymm}/components/toolchain/${project}-linaro/${base}"
echo "https://releases.linaro.org/archive/${yymm}/components/toolchain/${project}-linaro"
;;
kernel.org)
# TBD move to linux.sh?
if [ "${project}" != "linux" ]; then
CT_Abort "Unsupported project"
fi
local version="${CT_LINUX_VERSION}"
case "${version}" in
'')
# Ignore, this happens before .config is fully evaluated
;;
[34].*)
echo "http://www.kernel.org/pub/linux/kernel/v${version%%.*}.x"
;;
2.6.*)
echo "http://www.kernel.org/pub/linux/kernel/v2.6"
case "${version}" in
2.6.*.*)
echo "http://www.kernel.org/pub/linux/kernel/v2.6/longterm"
echo "http://www.kernel.org/pub/linux/kernel/v2.6/longterm/v${version%.*}"
;;
esac
;;
*)
CT_Abort "Unsupported Linux kernel version '${version}'"
;;
esac
;;
*)
CT_Abort "Unknown mirror '${org}'"
;;
esac
}
# Get most recent version for CVS check-out.
# CVS does not have a repository-wide identifier for a commit, so we must
# use date. Variables are set by CT_PackageRun
CT_GetVersion_cvs()
{
# If date is not given, use current. Otherwise, check if format is correct.
# We don't support fancy CVS specifications like "1 day ago", as we'll need
# to convert them to some stable representation like 20170617231304.
if [ -z "${devel_revision}" ]; then
devel_revision=`LANG=C TZ=UTC date '+%Y/%m/%d %H:%M:%S'`
else
case "${devel_revision}" in
[12][0-9][0-9][0-9]/[01][0-9]/[0-3][0-9]\ [0-2][0-9]:[0-5][0-9]:[0-5][0-9])
;;
*)
CT_Abort "${pkg_name}: invalid date format ${devel_revision}"
;;
esac
fi
basename="${pkg_name}-${devel_branch:-trunk}-${devel_revision//[^0-9]/}"
}
# Check out sources from CVS. Variables are set by CT_PackageRun.
CT_Download_cvs()
{
local pserver="${devel_url%% *}"
local module="${devel_url##* }"
# CVS has no name for "main" branch, so use -r only if non-default
# TBD try -'d ${basename}', with/without -N
CT_DoExecLog ALL cvs -z 9 -d "${pserver}" co -P ${devel_branch:+-r ${devel_branch}} \
-D "${devel_revision} UTC" "${module}"
if [ "${module}" != "${pkg_name}" ]; then
CT_DoExecLog ALL mv "${module}" "${pkg_name}"
fi
}
# Find the most recent version from Subversion.
CT_GetVersion_svn()
{
local version
devel_branch="${devel_branch:-/trunk}"
# If revision is not given, find the most recent
if [ -z "${devel_revision}" ]; then
devel_revision=`svn info "${devel_url}${devel_branch}" | sed -n 's/^Last Changed Rev: //p'`
fi
# Construct version from branch/revision
version="${devel_branch//\//_}"
version="${version#_}"
version="${version%_}"
version="${version}-${devel_revision}"
basename="${pkg_name}-${version}"
}
# Retrieve sources from Subversion.
CT_Download_svn()
{
CT_DoExecLog ALL svn export -r "${devel_revision}" "${devel_url}${devel_branch}" "${pkg_name}"
}
# Find the most recent version from Mercurial.
CT_GetVersion_hg()
{
if [ -n "${devel_branch}" -a -n "${devel_revision}" ]; then
CT_Abort "${pkg_name}: cannot specify both branch and changeset for Mercurial"
fi
# Mercurial cannot query remote branches except the default, so we'll have
# to clone if cset is not known and a branch is given.
if [ -z "${devel_revision}" ]; then
if [ -z "${devel_branch}" ]; then
# Mercurial does not allow querying branches
devel_revision=`hg identify "${devel_url}"`
else
CT_DoLog WARN "${pkg_name}: Mercurial cannot query non-default branch, will clone"
devel_revision="TBD"
fi
fi
basename="${pkg_name}-${devel_revision}"
}
# Retrieve sources from Mercurial.
CT_Download_hg()
{
CT_DoExecLog ALL hg clone "${devel_url}" "${pkg_name}"
CT_Pushd "${pkg_name}"
if [ -n "${devel_branch}" ]; then
CT_DoExecLog ALL hg update "${devel_branch}"
fi
if [ "${devel_revision}" = "TBD" ]; then
# Report what we found out (as common message lacks the revision)
devel_revision=`hg identify -i`
basename="${pkg_name}-${devel_revision}"
CT_DoLog EXTRA "Retrieved revision ${devel_revision}"
else
CT_DoExecLog ALL hg update "${devel_revision}"
fi
CT_DoExecLog ALL rm -rf .hg
CT_Popd
}
# Get the most recent version from Git.
CT_GetVersion_git()
{
if [ -n "${devel_branch}" -a -n "${devel_revision}" ]; then
CT_Abort "${pkg_name}: cannot specify both branch and changeset for Git"
fi
devel_branch="${devel_branch:-master}"
if [ -z "${devel_revision}" ]; then
local matches=`git ls-remote --exit-code "${devel_url}" --refs "${devel_branch}" \
|| echo "not found"`
local best using ref
# Cannot test $?, setting a trap on ERR prevents bash from returning the
# status code.
if [ "${matches}" = "not found" ]; then
CT_Abort "Failed to find git ref ${devel_branch} at ${devel_url}"
fi
if [ `echo "${matches}" | wc -l` -gt 1 ]; then
if echo "${matches}" | grep '[[:space:]]\(refs/heads/\)\?'"${devel_branch}\$" >/dev/null; then
# Try exact match, or prepended with "refs/heads". Some projects (e.g. binutils)
# have refs/original/refs/heads/master as well as refs/heads/master, and
# `git ls-remote refs/heads/master` prints both.
best=`echo "${matches}" | grep '[[:space:]]\(refs/heads/\)\?'"${devel_branch}\$"`
using="best match"
else
best=`echo "${matches}" | head -n1`
using="first"
fi
ref=`echo "${best}" | sed 's/.*[[:space:]]//'`
CT_DoLog WARN "Ambiguous ref ${devel_branch} at ${devel_url}, using ${using} (${ref})"
else
best="${matches}"
fi
devel_revision=`echo "${best}" | cut -c1-8`
CT_DoLog DEBUG "ref ${devel_branch} at ${devel_url} has cset of ${devel_revision}"
fi
basename="${pkg_name}-${devel_revision}"
}
# Retrieve sources from Git.
CT_Download_git()
{
# Git does not allow making a shallow clone of a specific commit.
CT_DoExecLog ALL git clone "${devel_url}" "${pkg_name}"
CT_Pushd "${pkg_name}"
CT_DoExecLog ALL git checkout "${devel_revision}" --
CT_DoExecLog ALL rm -rf .git
CT_Popd
}
# Helper: run another action after setting local variables
CT_PackageRun()
{
local sym="${1}"
local run="${2}"
local v
# Get rid of our arguments
shift 2
# Variables that are per-project
for v in use dir_name; do
eval "local ${v}=\${CT_${sym}_${v^^}}"
done
# If $use is not set, we only have one fork to handle
use="${use:-${sym}}"
# Variables that are per-fork
for v in basename pkg_name version src_release mirrors src_devel src_custom \
devel_vcs devel_url devel_branch devel_revision devel_subdir devel_bootstrap \
custom_location; do
eval "local ${v}=\${CT_${use}_${v^^}}"
done
${run} "$@"
# Save certain variables that may be modified by the callback.
# Fetching the sources is run in the main process, so no need to
# use CT_EnvModify.
for v in devel_branch devel_revision basename; do
eval "CT_${use}_${v^^}=\${${v}}"
done
}
# Closure for fetching the sources
CT_DoFetch()
{
local tmp_dir
if [ "${src_release}" = "y" ]; then
basename="${pkg_name}-${version}"
if ! CT_GetFile "${basename}" ${mirrors}; then
CT_Abort "${pkg_name}: download failed"
fi
elif [ "${src_devel}" = "y" ]; then
if [ -z "${devel_revision}" -a "${CT_FORBID_DOWNLOAD}" = "y" ]; then
CT_Abort "${pkg_name}: cannot find most recent revisions with downloads prohibited"
fi
# Each VCS backend must provide two methods:
# - CT_GetVersion_xxx that sets the base name for the package (package name
# and some unique identifier for the version)
# - CT_Download_xxx that retrieves the sources into the directory named as
# ${pkg_name}
# Both these methods can also modify devel_branch/devel_revision. Typically,
# this would override empty (default) values with "default branch name" and
# "most current revision", respectively.
CT_GetVersion_${devel_vcs}
if [ -z "${basename}" ]; then
CT_Abort "${pkg_name}: ${devel_vcs} did not set base name"
fi
# Try getting the tarball with empty list of URLs: it will only
# attempt getting it from local storage or from the mirror if configured.
# TBD extra "Retrieving" message from CT_GetFile
if CT_GetFile "${basename}" .tar.bz2; then
return 0
fi
CT_DoLog EXTRA "Retrieving '${basename}' (${devel_vcs} ${devel_url} ${devel_branch} ${devel_revision})"
if [ "${CT_FORBID_DOWNLOAD}" = "y" ]; then
CT_DoLog WARN "Downloads forbidden, not trying ${devel_vcs} retrieval"
return 1
fi
CT_MktempDir tmp_dir
CT_Pushd "${tmp_dir}"
CT_Download_${devel_vcs}
CT_DoExecLog ALL mv "${pkg_name}${devel_subdir:+/${devel_subdir}}" "${basename}"
# Post-download scripts.
# FIXME: This currently means we end up using host's autotools,
# but changing this requires reworking the order of operations in crosstool-NG:
# we'd need to defer the download/extraction/patching of a package until after
# the companion tools are built.
local remains="${devel_bootstrap}"
while [ -n "${remains}" ]; do
CT_Pushd "${basename}"
CT_DoExecLog ALL ${remains%%&&*}
case "${remains}" in
*\&\&*) remains="${remains#*&&}";;
*) remains=;;
esac
CT_Popd
done
CT_DoExecLog ALL tar cjf "${CT_TARBALLS_DIR}/${basename}.tar.bz2" "${basename}"
CT_SaveLocal "${CT_TARBALLS_DIR}/${basename}.tar.bz2"
CT_Popd
CT_DoExecLog ALL rm -rf "${tmp_dir}"
elif [ "${src_custom}" = "y" ]; then
# Will be handled during extraction/patching
:;
else
CT_Abort "No known source for ${pkg_name}"
fi
}
# Obtain the sources for a component, either from a tarball, version control system
# or a custom location.
CT_Fetch()
{
CT_PackageRun "${1}" CT_DoFetch
}
# Unpack an archive.
CT_Extract()
{
local file="${1}"
local dir="${2}"
local components="${3}"
CT_DoExecLog ALL mkdir -p "${dir}"
case "${file}" in
*.tar.xz)
xz -fdc "${file}" | CT_DoExecLog FILE tar x -f - -C "${dir}" ${components}
;;
*.tar.lzma)
xz -fdc "${file}" | CT_DoExecLog FILE tar x -f - -C "${dir}" ${components}
;;
*.tar.bz2)
bzip2 -dc "${file}" | CT_DoExecLog FILE tar x -f - -C "${dir}" ${components}
;;
*.tar.gz|*.tgz)
gzip -dc "${file}" | CT_DoExecLog FILE tar x -f - -C "${dir}" ${components}
;;
*.tar)
CT_DoExecLog FILE tar x -f "${file}" -C "${dir}" ${components}
;;
*.zip)
CT_Pushd "${dir}"
CT_DoExecLog FILE unzip "${file}" ${components}
CT_Popd
;;
*)
CT_Abort "Don't know how to handle ${file}: unknown extension"
;;
esac
}
# Closure for unpacking/patching the sources. There are two source directories:
# - CT_COMMON_SRC_DIR stores common sources, such as released tarballs (including
# bundled or local patches, if necessary) or checked out working copies.
# Custom sources cannot be placed here, as they may have similarly named
# packages coming from different origins.
# - CT_SRC_DIR stores per-configuration sources. These are either symlinks back
# to CT_ORIG_SRC_DIR sources, or a copy from the custom source, or a copy
# from CT_ORIG_SRC_DIR + target-specific overrides.
CT_DoExtractPatch()
{
local patchfunc="${1}"
local ext
local -a patch_dirs
local bundled_patch_dir
local local_patch_dir
if [ "${src_custom}" != "y" ]; then
# Non-custom: extract to shared location
# If the previous extraction/patching was aborted, clean up.
if [ -r "${CT_COMMON_SRC_DIR}/.${basename}.extracting" -o \
-r "${CT_COMMON_SRC_DIR}/.${basename}.patching" ]; then
CT_DoLog WARN "Sources for ${basename} were partially extracted/patched, cleaning up"
CT_DoExecLog ALL rm -rf "${CT_COMMON_SRC_DIR}/${basename}"
CT_DoExecLog ALL rm -f "${CT_COMMON_SRC_DIR}/.${basename}".*
fi
if [ -f "${CT_COMMON_SRC_DIR}/.${basename}.extracted" ]; then
CT_DoLog DEBUG "Already extracted ${basename}"
else
CT_DoLog EXTRA "Extracting ${basename}"
CT_DoExecLog ALL touch "${CT_COMMON_SRC_DIR}/.${basename}.extracting"
# TBD save/discover the extension while fetching
ext=`CT_GetFileExtension "${basename}"`
CT_Extract "${CT_TARBALLS_DIR}/${basename}${ext}" "${CT_COMMON_SRC_DIR}"
CT_DoExecLog ALL touch "${CT_COMMON_SRC_DIR}/.${basename}.extracted"
CT_DoExecLog ALL rm -f "${CT_COMMON_SRC_DIR}/.${basename}.extracting"
fi
fi
# Patch the released tarballs
if [ "${src_release}" = "y" ]; then
if [ -f "${CT_COMMON_SRC_DIR}/.${basename}.patched" ]; then
CT_DoLog DEBUG "Already patched ${basename}"
else
CT_DoLog EXTRA "Patching ${basename}"
CT_DoExecLog ALL touch "${CT_COMMON_SRC_DIR}/.${basename}.patching"
bundled_patch_dir="${CT_LIB_DIR}/packages/${pkg_name}/${version}"
local_patch_dir="${CT_LOCAL_PATCH_DIR}/${pkg_name}/${version}"
case "${CT_PATCH_ORDER}" in
bundled) patch_dirs=("${bundled_patch_dir}");;
local) patch_dirs=("${local_patch_dir}");;
bundled,local) patch_dirs=("${bundled_patch_dir}" "${local_patch_dir}");;
local,bundled) patch_dirs=("${local_patch_dir}" "${bundled_patch_dir}");;
none) patch_dirs=;;
esac
CT_Pushd "${CT_COMMON_SRC_DIR}/${basename}"
for d in "${patch_dirs[@]}"; do
CT_DoLog DEBUG "Looking for patches in '${d}'..."
if [ -n "${d}" -a -d "${d}" ]; then
for p in "${d}"/*.patch; do
if [ -f "${p}" ]; then
CT_DoExecLog ALL ${patch} --no-backup-if-mismatch -g0 -F1 -p1 -f -i "${p}"
fi
done
fi
done
# TBD meta-package for config.sub/config.guess with replacement script
if [ "${CT_OVERRIDE_CONFIG_GUESS_SUB}" = "y" ]; then
CT_DoLog ALL "Overiding config.guess and config.sub"
for cfg in config.guess config.sub; do
# Can't use CT_DoExecLog because of the '{} \;' to be passed un-mangled to find
find . -type f -name "${cfg}" \
-exec chmod -v u+w {} \; \
-exec cp -v "${CT_TOP_DIR}/scripts/${cfg}" {} \; |CT_DoLog ALL
done
fi
if [ -n "${patchfunc}" ]; then
${patchfunc}
fi
CT_Popd
CT_DoExecLog ALL touch "${CT_COMMON_SRC_DIR}/.${basename}.patched"
CT_DoExecLog ALL rm -f "${CT_COMMON_SRC_DIR}/.${basename}.patching"
fi
else
CT_DoLog WARN "${pkg_name}: not using a released version, no patches applied"
fi
# Symlink/copy/overlay into per-target source directory
if [ "${src_custom}" = "y" ]; then
# Custom sources: unpack or copy into per-target directory
if [ "${CT_TARGET_USE_OVERLAY}" ]; then
CT_DoLog WARN "${pkg_name}: using custom location, no overlays applied"
fi
if [ -d "${custom_location}" ]; then
CT_DoExecLog ALL cp -av "${custom_location}" "${CT_SRC_DIR}/${pkg_name}"
elif [ -f "${custom_location}" ]; then
# Assume "foo.tar.gz" (or likes) contain the "foo" directory
local bn
CT_Extract "${custom_location}" "${CT_SRC_DIR}"
bn=`CT_GetFileBasename "${custom_location##*/}"`
CT_TestOrAbort "Unknown file extension: ${custom_location}" -n "${bn}"
CT_DoExecLog ALL mv -v "${CT_SRC_DIR}/${bn%${ext}}" "${CT_SRC_DIR}/${pkg_name}"
else
CT_Abort "Neither file nor directory: ${custom_location}"
fi
elif [ "${CT_TARGET_USE_OVERLAY}" = "y" ]; then
# Possibly has overlays; copy from common source and apply overlays if any
local overlay
CT_DoExecLog ALL cp -av "${CT_COMMON_SRC_DIR}/${basename}" "${CT_SRC_DIR}/${pkg_name}"
overlay="${CT_OVERLAY_LOCATION}/${CT_ARCH}-${CT_OVERLAY_NAME:-overlay}"
ext=`CT_GetFileExtension "${overlay}"`
CT_Extract "${overlay}${ext}" "${CT_SRC_DIR}" "${pkg_name}"
else
# Common source, just symlink
CT_DoExecLog ALL ln -s "${CT_COMMON_SRC_DIR}/${basename}" "${CT_SRC_DIR}/${pkg_name}"
fi
}
# Extract/copy the sources to the shared source directory, then either symlink
# or copy the sources into a private source directory and apply target-specific
# changes (such as xtensa overrides).
CT_ExtractPatch()
{
local pkg="${1}"
shift
CT_PackageRun "${pkg}" CT_DoExtractPatch "$@"
}