mirror of
https://github.com/LibreELEC/LibreELEC.tv
synced 2025-09-24 19:46:01 +07:00
This function can be used on "autotools" and "configure" packages.
Use libtool_remove_rpath() to remove hardcode rpath when --disable-rpath
is not supported by "configure".
usage: `libtool_remove_rpath libtool`
If there are multiple libtool scripts or they are in subdirectories,
then you may need to run this function using the path to libtool or run
the function multiple times.
If binaries and shared objects installed by the package to target have
incorrect or system libraries then this function can be used. After
using the function, these example command below should NOT have a
RPATH/RUNPATH.
e.g. (before using the fucntion)
$ readelf --dynamic ${filename} | grep PATH
RPATH Library runpath: [.....]
RUNPATH Library runpath: [.....]
Before using this function; check if the package supports
--disable-rpath (and that it works.) Work with upstream and have a
working --disable-rpath added to the package.
1753 lines
56 KiB
Plaintext
1753 lines
56 KiB
Plaintext
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
# Copyright (C) 2018-present Team LibreELEC (https://libreelec.tv)
|
|
|
|
### FUNCTION HELPERS ###
|
|
# die (message, code) abort with optional message and code
|
|
die() {
|
|
if [ -n "$1" ]; then
|
|
echo -e "$1" >&2
|
|
fi
|
|
exit "${2:-1}"
|
|
}
|
|
|
|
onexitcleanup() {
|
|
[ $? -eq 0 ] && return
|
|
|
|
local _BASH_COMMAND="${BASH_COMMAND}"
|
|
|
|
if [ -n "${PKG_CURRENT_CALL}" ]; then
|
|
print_color CLR_ERROR "FAILURE: $* during ${PKG_CURRENT_CALL} (${PKG_CURRENT_CALL_TYPE})"
|
|
echo
|
|
fi
|
|
|
|
if [ -n "${_BASH_COMMAND}" ]; then
|
|
if [[ ! ${_BASH_COMMAND} =~ ^exit\ ]] && [[ ! ${_BASH_COMMAND} =~ ^return\ ]]; then
|
|
echo "*********** FAILED COMMAND ***********"
|
|
echo "${_BASH_COMMAND}"
|
|
echo "**************************************"
|
|
fi
|
|
fi
|
|
}
|
|
[ "${NOONEXIT}" != "yes" ] && trap "onexitcleanup $0 $@" EXIT
|
|
|
|
# return 0 if $2 in space-separated list $1, otherwise return 1
|
|
listcontains() {
|
|
if [ -n "$1" -a -n "$2" ]; then
|
|
[[ ${1} =~ (^|[[:space:]])${2}($|[[:space:]]) ]] && return 0 || return 1
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# remove item(s) from list.
|
|
# looping makes it greedy (eg. listremoveitem "abc def ghi" "(abc|def)" removes both "abc" and "def").
|
|
listremoveitem() {
|
|
local data="${1}" odata tmp_array
|
|
if [ -n "$1" -a -n "$2" ]; then
|
|
while [ : ]; do
|
|
odata="${data}"
|
|
data="$(echo "${data}" | sed -E "s (^|[[:space:]])${2}($|[[:space:]]) \ g")"
|
|
[ "${odata}" = "${data}" ] && break
|
|
done
|
|
fi
|
|
# Use array word splitting to squash spaces
|
|
tmp_array=(${data})
|
|
echo "${tmp_array[@]}"
|
|
}
|
|
|
|
print_color() {
|
|
local clr_name="$1" clr_text="$2" clr_actual
|
|
local black red green yellow blue magenta cyan white endcolor
|
|
local boldblack boldred boldgreen boldyellow boldblue boldmagenta boldcyan boldwhite
|
|
|
|
[ -z "${clr_name}" ] && return 0
|
|
|
|
if [ "$DISABLE_COLORS" = "yes" ]; then
|
|
[ $# -eq 2 ] && echo -en "${clr_text}"
|
|
return 0
|
|
fi
|
|
|
|
black="\e[0;30m"
|
|
boldblack="\e[1;30m"
|
|
red="\e[0;31m"
|
|
boldred="\e[1;31m"
|
|
green="\e[0;32m"
|
|
boldgreen="\e[1;32m"
|
|
yellow="\e[0;33m"
|
|
boldyellow="\e[1;33m"
|
|
blue="\e[0;34m"
|
|
boldblue="\e[1;34m"
|
|
magenta="\e[0;35m"
|
|
boldmagenta="\e[1;35m"
|
|
cyan="\e[0;36m"
|
|
boldcyan="\e[1;36m"
|
|
white="\e[0;37m"
|
|
boldwhite="\e[1;37m"
|
|
endcolor="\e[0m"
|
|
|
|
# $clr_name can be a color variable (boldgreen etc.) or a
|
|
# "standard" color determined by an indirect name (CLR_ERROR etc.)
|
|
#
|
|
# If ${!clr_name} doesn't exist then assume it's a standard color.
|
|
# If ${!clr_name} does exist then check it's not a custom color mapping.
|
|
# Custom color mappings can be configured in options files.
|
|
#
|
|
clr_actual="${!clr_name}"
|
|
|
|
if [ -n "${clr_actual}" ]; then
|
|
clr_actual="${!clr_actual}"
|
|
else
|
|
case "${clr_name}" in
|
|
CLR_ERROR) clr_actual="${boldred}";;
|
|
CLR_WARNING) clr_actual="${boldred}";;
|
|
CLR_WARNING_DIM) clr_actual="${red}";;
|
|
|
|
CLR_APPLY_PATCH) clr_actual="${boldgreen}";;
|
|
CLR_AUTORECONF) clr_actual="${boldmagenta}";;
|
|
CLR_BUILD) clr_actual="${boldyellow}";;
|
|
CLR_TOOLCHAIN) clr_actual="${boldmagenta}";;
|
|
CLR_CLEAN) clr_actual="${boldred}";;
|
|
CLR_FIXCONFIG) clr_actual="${boldyellow}";;
|
|
CLR_GET) clr_actual="${boldcyan}";;
|
|
CLR_INFO) clr_actual="${boldgreen}";;
|
|
CLR_INSTALL) clr_actual="${boldgreen}";;
|
|
CLR_PATCH_DESC) clr_actual="${boldwhite}";;
|
|
CLR_TARGET) clr_actual="${boldwhite}";;
|
|
CLR_UNPACK) clr_actual="${boldcyan}";;
|
|
CLR_AUTOREMOVE) clr_actual="${boldblue}";;
|
|
|
|
CLR_ENDCOLOR) clr_actual="${endcolor}";;
|
|
|
|
*) clr_actual="${endcolor}";;
|
|
esac
|
|
fi
|
|
|
|
if [ $# -eq 2 ]; then
|
|
echo -en "${clr_actual}${clr_text}${endcolor}"
|
|
else
|
|
echo -en "${clr_actual}"
|
|
fi
|
|
}
|
|
|
|
# print build progress messages
|
|
# param1: message color, p2: label, p3: text, p4: indent (optional)
|
|
build_msg() {
|
|
local spaces
|
|
|
|
[ -n "${BUILD_INDENT}" ] && spaces="$(printf "%${BUILD_INDENT}c" " ")" || spaces=""
|
|
|
|
if [ -n "${3}" ]; then
|
|
echo -e "${spaces}$(print_color "${1}" "${2}") ${3}" >&${SILENT_OUT}
|
|
else
|
|
echo -e "${spaces}$(print_color "${1}" "${2}")" >&${SILENT_OUT}
|
|
fi
|
|
|
|
# pad left space to create "indent" effect
|
|
if [ "${4}" = "indent" ]; then
|
|
export BUILD_INDENT=$((${BUILD_INDENT:-0}+${BUILD_INDENT_SIZE}))
|
|
elif [ -n "${4}" ]; then
|
|
die "ERROR: ${0} unexpected parameter: ${4}"
|
|
fi
|
|
}
|
|
|
|
# prints a warning if the file slated for removal doesn't exist
|
|
# this allows us to continue instead of bailing out with just "rm"
|
|
safe_remove() {
|
|
local path
|
|
|
|
for path in "$@" ; do
|
|
if [ -e "${path}" -o -L "${path}" ]; then
|
|
rm -r "${path}"
|
|
elif [ -n "${PKG_NAME}" ]; then
|
|
print_color CLR_WARNING "safe_remove: path does not exist: [${PKG_NAME}]: ${path}\n"
|
|
else
|
|
print_color CLR_WARNING "safe_remove: path does not exist: ${path}\n"
|
|
fi
|
|
done
|
|
}
|
|
|
|
### BUILDSYSTEM HELPERS ###
|
|
# check if a flag is enabled
|
|
# $1: flag-name, $2: default (yes/no), $3: ingenious check (none,only-disable,only-enable)
|
|
# return 0 if flag is enabled, otherwise 1
|
|
flag_enabled() {
|
|
# check flag
|
|
if [ -n "${PKG_BUILD_FLAGS}" ] && listcontains "${PKG_BUILD_FLAGS}" "[+]?$1"; then
|
|
if [ "${3:none}" = "only-disable" ]; then
|
|
die "ERROR: $1 cannot enable via PKG_BUILD_FLAGS (found in $PKG_NAME)"
|
|
fi
|
|
return 0
|
|
elif [ "$2" = "yes" ] && ! listcontains "${PKG_BUILD_FLAGS}" "-$1"; then
|
|
return 0
|
|
else
|
|
if [ "${3:none}" = "only-enable" ]; then
|
|
die "ERROR: $1 cannot disable via PKG_BUILD_FLAGS (found in $PKG_NAME)"
|
|
fi
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
setup_pkg_config_target() {
|
|
export PKG_CONFIG="$TOOLCHAIN/bin/pkg-config"
|
|
export PKG_CONFIG_PATH=""
|
|
export PKG_CONFIG_LIBDIR="$SYSROOT_PREFIX/usr/lib/pkgconfig:$SYSROOT_PREFIX/usr/share/pkgconfig"
|
|
export PKG_CONFIG_SYSROOT_BASE="$BUILD"
|
|
export PKG_CONFIG_SYSROOT_DIR="$SYSROOT_PREFIX"
|
|
export PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=1
|
|
export PKG_CONFIG_ALLOW_SYSTEM_LIBS=1
|
|
}
|
|
|
|
setup_pkg_config_host() {
|
|
export PKG_CONFIG="$TOOLCHAIN/bin/pkg-config"
|
|
export PKG_CONFIG_PATH=""
|
|
export PKG_CONFIG_LIBDIR="$TOOLCHAIN/lib/pkgconfig:$TOOLCHAIN/share/pkgconfig"
|
|
export PKG_CONFIG_SYSROOT_BASE=""
|
|
export PKG_CONFIG_SYSROOT_DIR=""
|
|
unset PKG_CONFIG_ALLOW_SYSTEM_CFLAGS
|
|
unset PKG_CONFIG_ALLOW_SYSTEM_LIBS
|
|
}
|
|
|
|
check_toolchain_config() {
|
|
local target=${1^^}
|
|
local toolchain=${2^^}
|
|
local var
|
|
if [ "${toolchain}" == "AUTOTOOLS" ]; then
|
|
toolchain="CONFIGURE"
|
|
fi
|
|
if [ "${toolchain}" == "CMAKE-MAKE" ]; then
|
|
toolchain="CMAKE"
|
|
fi
|
|
for var in "${!PKG_@}"; do
|
|
if [[ "${var}" =~ INSTALL_OPTS_ || "${var}" =~ _MAKE_OPTS || "${var}" =~ _TAR_COPY_OPTS ]]; then
|
|
continue
|
|
fi
|
|
if [[ "${var}" =~ _OPTS_${target}$ \
|
|
&& ! "${var}" =~ _${toolchain}_OPTS_${target}$
|
|
|| "${var}" =~ _OPTS$ && ! "${var}" =~ _${toolchain}_OPTS$ ]]; then
|
|
die "ERROR: using $2 toolchain but ${var} is configured."
|
|
fi
|
|
done
|
|
}
|
|
|
|
setup_toolchain() {
|
|
local have_gold="no"
|
|
if [ "$LTO_SUPPORT" = "yes" ]; then
|
|
if flag_enabled "lto-parallel" "no"; then
|
|
TARGET_CFLAGS+=" $FLAGS_OPTIM_LTO_PARALLEL $FLAGS_OPTIM_LTO_NO_FAT"
|
|
TARGET_CXXFLAGS+=" $FLAGS_OPTIM_LTO_PARALLEL $FLAGS_OPTIM_LTO_NO_FAT"
|
|
TARGET_LDFLAGS+=" $LDFLAGS_OPTIM_LTO_COMMON $FLAGS_OPTIM_LTO_PARALLEL"
|
|
elif flag_enabled "lto-fat" "no"; then
|
|
TARGET_CFLAGS+=" $FLAGS_OPTIM_LTO_NO_PARALLEL $FLAGS_OPTIM_LTO_FAT"
|
|
TARGET_CXXFLAGS+=" $FLAGS_OPTIM_LTO_NO_PARALLEL $FLAGS_OPTIM_LTO_FAT"
|
|
TARGET_LDFLAGS+=" $LDFLAGS_OPTIM_LTO_COMMON $FLAGS_OPTIM_LTO_NO_PARALLEL"
|
|
elif flag_enabled "lto" "no"; then
|
|
TARGET_CFLAGS+=" $FLAGS_OPTIM_LTO_NO_PARALLEL $FLAGS_OPTIM_LTO_NO_FAT"
|
|
TARGET_CXXFLAGS+=" $FLAGS_OPTIM_LTO_NO_PARALLEL $FLAGS_OPTIM_LTO_NO_FAT"
|
|
TARGET_LDFLAGS+=" $LDFLAGS_OPTIM_LTO_COMMON $FLAGS_OPTIM_LTO_NO_PARALLEL"
|
|
fi
|
|
fi
|
|
|
|
if flag_enabled "lto-off" "no"; then
|
|
TARGET_CFLAGS+=" $FLAGS_OPTIM_LTO_OFF"
|
|
TARGET_CXXFLAGS+=" $FLAGS_OPTIM_LTO_OFF"
|
|
TARGET_LDFLAGS+=" $FLAGS_OPTIM_LTO_OFF"
|
|
fi
|
|
|
|
# gold flag
|
|
if flag_enabled "gold" "$GOLD_SUPPORT" "only-disable"; then
|
|
TARGET_LDFLAGS+=" $LDFLAGS_OPTIM_GOLD"
|
|
have_gold="yes"
|
|
fi
|
|
|
|
# compiler optimization, descending priority: speed, size, default
|
|
if [ "${BUILD_WITH_DEBUG}" = "yes" ]; then
|
|
if [ "${SPLIT_DEBUG_INFO}" = "yes" -a "${have_gold}" = "yes" ]; then
|
|
TARGET_CFLAGS+=" $CFLAGS_OPTIM_DEBUG_SPLIT"
|
|
TARGET_CXXFLAGS+=" $CXXFLAGS_OPTIM_DEBUG_SPLIT"
|
|
TARGET_LDFLAGS+=" $LDFLAGS_OPTIM_DEBUG_SPLIT"
|
|
else
|
|
TARGET_CFLAGS+=" $CFLAGS_OPTIM_DEBUG"
|
|
TARGET_CXXFLAGS+=" $CXXFLAGS_OPTIM_DEBUG"
|
|
TARGET_LDFLAGS+=" $LDFLAGS_OPTIM_DEBUG"
|
|
fi
|
|
elif flag_enabled "speed" "no"; then
|
|
TARGET_CFLAGS+=" $CFLAGS_OPTIM_SPEED"
|
|
TARGET_CXXFLAGS+=" $CXXFLAGS_OPTIM_SPEED"
|
|
elif flag_enabled "size" "no"; then
|
|
TARGET_CFLAGS+=" $CFLAGS_OPTIM_SIZE"
|
|
TARGET_CXXFLAGS+=" $CXXFLAGS_OPTIM_SIZE"
|
|
else
|
|
TARGET_CFLAGS+=" $CFLAGS_OPTIM_DEFAULT"
|
|
TARGET_CXXFLAGS+=" $CXXFLAGS_OPTIM_DEFAULT"
|
|
fi
|
|
|
|
# position-independent code
|
|
if flag_enabled "pic" "no"; then
|
|
TARGET_CFLAGS+=" $CFLAGS_OPTIM_PIC"
|
|
TARGET_CXXFLAGS+=" $CXXFLAGS_OPTIM_PIC"
|
|
TARGET_LDFLAGS+=" $LDFLAGS_OPTIM_PIC"
|
|
fi
|
|
if flag_enabled "pic:host" "no"; then
|
|
HOST_CFLAGS+=" $CFLAGS_OPTIM_PIC"
|
|
HOST_CXXFLAGS+=" $CXXFLAGS_OPTIM_PIC"
|
|
HOST_LDFLAGS+=" $LDFLAGS_OPTIM_PIC"
|
|
fi
|
|
|
|
# hardening support
|
|
if flag_enabled "hardening" "$HARDENING_SUPPORT"; then
|
|
TARGET_CFLAGS+=" $CFLAGS_OPTIM_HARDENING"
|
|
TARGET_CXXFLAGS+=" $CXXFLAGS_OPTIM_HARDENING"
|
|
TARGET_CFLAGS+=" $CPPFLAGS_OPTIM_HARDENING"
|
|
TARGET_LDFLAGS+=" $LDFLAGS_OPTIM_HARDENING"
|
|
fi
|
|
|
|
# parallel
|
|
if flag_enabled "parallel" "yes"; then
|
|
NINJA_OPTS="-j$CONCURRENCY_MAKE_LEVEL"
|
|
MAKEFLAGS="-j$CONCURRENCY_MAKE_LEVEL"
|
|
else
|
|
NINJA_OPTS="-j1"
|
|
MAKEFLAGS="-j1"
|
|
fi
|
|
|
|
# verbose flag
|
|
if flag_enabled "verbose" "no"; then
|
|
NINJA_OPTS+=" -v"
|
|
MAKEFLAGS+=" V=1 VERBOSE=1"
|
|
fi
|
|
|
|
# average load limit
|
|
if [ "${CONCURRENCY_LOAD}" != "0" ]; then
|
|
NINJA_OPTS+=" -l${CONCURRENCY_LOAD}"
|
|
MAKEFLAGS+=" -l${CONCURRENCY_LOAD}"
|
|
fi
|
|
export MAKEFLAGS
|
|
|
|
case "$1:$2" in
|
|
target:meson|init:meson)
|
|
export DESTIMAGE="target"
|
|
export AWK="gawk"
|
|
export CC="$TOOLCHAIN/bin/host-gcc"
|
|
export CXX="$TOOLCHAIN/bin/host-g++"
|
|
export CPP="cpp"
|
|
export LD="ld"
|
|
export AS="as"
|
|
export AR="ar"
|
|
export NM="nm"
|
|
export RANLIB="ranlib"
|
|
export OBJCOPY="objcopy"
|
|
export OBJDUMP="objdump"
|
|
export STRIP="strip"
|
|
export CPPFLAGS="$HOST_CPPFLAGS"
|
|
export CFLAGS="$HOST_CFLAGS"
|
|
export CXXFLAGS="$HOST_CXXFLAGS"
|
|
export LDFLAGS="$HOST_LDFLAGS"
|
|
setup_pkg_config_target
|
|
export TARGET_CC="${TARGET_PREFIX}gcc"
|
|
export TARGET_CXX="${TARGET_PREFIX}g++"
|
|
export TARGET_AR="${TARGET_PREFIX}ar"
|
|
export TARGET_STRIP="${TARGET_PREFIX}strip"
|
|
export TARGET_CFLAGS="$TARGET_CFLAGS"
|
|
export TARGET_CXXFLAGS="$TARGET_CXXFLAGS"
|
|
export TARGET_LDFLAGS="$TARGET_LDFLAGS"
|
|
export HOST_CC="$CC"
|
|
export HOST_CXX="$CXX"
|
|
export HOSTCC="$CC"
|
|
export HOSTCXX="$CXX"
|
|
export CC_FOR_BUILD="$CC"
|
|
export CXX_FOR_BUILD="$CXX"
|
|
export BUILD_CC="$CC"
|
|
export BUILD_CXX="$CXX"
|
|
export _python_sysroot="$SYSROOT_PREFIX"
|
|
export _python_prefix=/usr
|
|
export _python_exec_prefix=/usr
|
|
;;
|
|
|
|
target:*|init:*)
|
|
export DESTIMAGE="target"
|
|
export CC="${TARGET_PREFIX}gcc"
|
|
export CXX="${TARGET_PREFIX}g++"
|
|
export CPP="${TARGET_PREFIX}cpp"
|
|
export LD="${TARGET_PREFIX}ld"
|
|
export AS="${TARGET_PREFIX}as"
|
|
export AR="${TARGET_PREFIX}ar"
|
|
export NM="${TARGET_PREFIX}nm"
|
|
export RANLIB="${TARGET_PREFIX}ranlib"
|
|
export OBJCOPY="${TARGET_PREFIX}objcopy"
|
|
export OBJDUMP="${TARGET_PREFIX}objdump"
|
|
export STRIP="${TARGET_PREFIX}strip"
|
|
export CPPFLAGS="$TARGET_CPPFLAGS"
|
|
export CFLAGS="$TARGET_CFLAGS"
|
|
export CXXFLAGS="$TARGET_CXXFLAGS"
|
|
export LDFLAGS="$TARGET_LDFLAGS"
|
|
setup_pkg_config_target
|
|
export CMAKE_CONF=$TOOLCHAIN/etc/cmake-$TARGET_NAME.conf
|
|
export CMAKE="cmake -DCMAKE_TOOLCHAIN_FILE=$CMAKE_CONF -DCMAKE_INSTALL_PREFIX=/usr"
|
|
if [ ! -f $CMAKE_CONF ] ; then
|
|
mkdir -p $TOOLCHAIN/etc
|
|
echo "SET(CMAKE_SYSTEM_NAME Linux)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_SYSTEM_VERSION 1)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_SYSTEM_PROCESSOR $TARGET_ARCH)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_C_COMPILER $CC)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_CXX_COMPILER $CXX)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_CPP_COMPILER $CPP)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_ASM_FLAGS_MINSIZEREL -DDUMMYOPT)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_CXX_FLAGS_MINSIZEREL -DDUMMYOPT)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_C_FLAGS_MINSIZEREL -DDUMMYOPT)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_FIND_ROOT_PATH $SYSROOT_PREFIX)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)" >> $CMAKE_CONF
|
|
if [ "${DISPLAYSERVER}" = "x11" ]; then
|
|
if [ "${OPENGL}" = "mesa" ] || listcontains "${GRAPHIC_DRIVERS}" "nvidia"; then
|
|
echo "SET(OpenGL_GL_PREFERENCE GLVND)" >> $CMAKE_CONF
|
|
fi
|
|
fi
|
|
fi
|
|
export HOST_CC="$TOOLCHAIN/bin/host-gcc"
|
|
export HOST_CXX="$TOOLCHAIN/bin/host-g++"
|
|
export HOSTCC="$HOST_CC"
|
|
export HOSTCXX="$HOST_CXX"
|
|
export CC_FOR_BUILD="$HOST_CC"
|
|
export CXX_FOR_BUILD="$HOST_CXX"
|
|
export BUILD_CC="$HOST_CC"
|
|
export BUILD_CXX="$HOST_CXX"
|
|
export _python_sysroot="$SYSROOT_PREFIX"
|
|
export _python_prefix=/usr
|
|
export _python_exec_prefix=/usr
|
|
;;
|
|
host:*|bootstrap:*)
|
|
export DESTIMAGE="host"
|
|
export AWK="gawk"
|
|
export CC="$TOOLCHAIN/bin/host-gcc"
|
|
export CXX="$TOOLCHAIN/bin/host-g++"
|
|
export CPP="cpp"
|
|
export LD="ld"
|
|
export AS="as"
|
|
export AR="ar"
|
|
export NM="nm"
|
|
export RANLIB="ranlib"
|
|
export OBJCOPY="objcopy"
|
|
export OBJDUMP="objdump"
|
|
export STRIP="strip"
|
|
export CPPFLAGS="$HOST_CPPFLAGS"
|
|
export CFLAGS="$HOST_CFLAGS"
|
|
export CXXFLAGS="$HOST_CXXFLAGS"
|
|
export LDFLAGS="$HOST_LDFLAGS"
|
|
setup_pkg_config_host
|
|
export CMAKE_CONF=$TOOLCHAIN/etc/cmake-$HOST_NAME.conf
|
|
export CMAKE="cmake -DCMAKE_TOOLCHAIN_FILE=$CMAKE_CONF -DCMAKE_INSTALL_PREFIX=$TOOLCHAIN"
|
|
if [ ! -f $CMAKE_CONF ] ; then
|
|
mkdir -p $TOOLCHAIN/etc
|
|
echo "SET(CMAKE_SYSTEM_NAME Linux)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_SYSTEM_VERSION 1)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_SYSTEM_PROCESSOR ${MACHINE_HARDWARE_NAME})" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_C_COMPILER $CC)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_CXX_COMPILER $CXX)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_CPP_COMPILER $CXX)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_ASM_FLAGS_RELEASE -DDUMMYOPT)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_CXX_FLAGS_RELEASE -DDUMMYOPT)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_C_FLAGS_RELEASE -DDUMMYOPT)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_AR $AR CACHE FILEPATH "Archiver")" >> $CMAKE_CONF # hum?
|
|
echo "SET(CMAKE_FIND_ROOT_PATH $TOOLCHAIN)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH)" >> $CMAKE_CONF
|
|
echo "SET(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH)" >> $CMAKE_CONF
|
|
fi
|
|
export HOST_CC="$CC"
|
|
export HOST_CXX="$CXX"
|
|
export HOSTCC="$CC"
|
|
export HOSTCXX="$CXX"
|
|
export CC_FOR_BUILD="$CC"
|
|
export CXX_FOR_BUILD="$CXX"
|
|
export BUILD_CC="$CC"
|
|
export BUILD_CXX="$CXX"
|
|
export _python_sysroot="$TOOLCHAIN"
|
|
export _python_prefix=/
|
|
export _python_exec_prefix=/
|
|
;;
|
|
esac
|
|
}
|
|
|
|
create_meson_conf_host() {
|
|
local properties
|
|
properties="PKG_MESON_PROPERTIES_${1^^}"
|
|
|
|
cat > $2 <<EOF
|
|
[binaries]
|
|
c = '$CC'
|
|
cpp = '$CXX'
|
|
ar = '$AR'
|
|
strip = '$STRIP'
|
|
pkgconfig = '$PKG_CONFIG'
|
|
llvm-config = '$TOOLCHAIN/bin/llvm-config-host'
|
|
libgcrypt-config = '$SYSROOT_PREFIX/usr/bin/libgcrypt-config'
|
|
|
|
[build_machine]
|
|
system = 'linux'
|
|
cpu_family = '${MACHINE_HARDWARE_NAME}'
|
|
cpu = '${MACHINE_HARDWARE_CPU}'
|
|
endian = 'little'
|
|
|
|
[host_machine]
|
|
system = 'linux'
|
|
cpu_family = '${MACHINE_HARDWARE_NAME}'
|
|
cpu = '${MACHINE_HARDWARE_CPU}'
|
|
endian = 'little'
|
|
|
|
[built-in options]
|
|
$(python3 -c "import os; print('c_args = {}'.format([x for x in os.getenv('CFLAGS').split()]))")
|
|
$(python3 -c "import os; print('c_link_args = {}'.format([x for x in os.getenv('LDFLAGS').split()]))")
|
|
$(python3 -c "import os; print('cpp_args = {}'.format([x for x in os.getenv('CXXFLAGS').split()]))")
|
|
$(python3 -c "import os; print('cpp_link_args = {}'.format([x for x in os.getenv('LDFLAGS').split()]))")
|
|
|
|
[properties]
|
|
root = '$TOOLCHAIN'
|
|
${!properties}
|
|
EOF
|
|
}
|
|
|
|
create_meson_conf_target() {
|
|
local properties
|
|
properties="PKG_MESON_PROPERTIES_${1^^}"
|
|
|
|
cat > $2 <<EOF
|
|
[binaries]
|
|
c = '$TARGET_CC'
|
|
cpp = '$TARGET_CXX'
|
|
ar = '$TARGET_AR'
|
|
strip = '$TARGET_STRIP'
|
|
pkgconfig = '$PKG_CONFIG'
|
|
llvm-config = '$TOOLCHAIN/bin/llvm-config-host'
|
|
libgcrypt-config = '$SYSROOT_PREFIX/usr/bin/libgcrypt-config'
|
|
|
|
[build_machine]
|
|
system = 'linux'
|
|
cpu_family = '${MACHINE_HARDWARE_NAME}'
|
|
cpu = '${MACHINE_HARDWARE_CPU}'
|
|
endian = 'little'
|
|
|
|
[host_machine]
|
|
system = 'linux'
|
|
cpu_family = '$TARGET_ARCH'
|
|
cpu = '$TARGET_SUBARCH'
|
|
endian = 'little'
|
|
|
|
[built-in options]
|
|
$(python3 -c "import os; print('c_args = {}'.format([x for x in os.getenv('TARGET_CFLAGS').split()]))")
|
|
$(python3 -c "import os; print('c_link_args = {}'.format([x for x in os.getenv('TARGET_LDFLAGS').split()]))")
|
|
$(python3 -c "import os; print('cpp_args = {}'.format([x for x in os.getenv('TARGET_CXXFLAGS').split()]))")
|
|
$(python3 -c "import os; print('cpp_link_args = {}'.format([x for x in os.getenv('TARGET_LDFLAGS').split()]))")
|
|
|
|
[properties]
|
|
needs_exe_wrapper = true
|
|
root = '$SYSROOT_PREFIX/usr'
|
|
${!properties}
|
|
EOF
|
|
}
|
|
|
|
|
|
# unset all PKG_* vars apart from those exported by setup_toolchain, then set default values
|
|
reset_pkg_vars() {
|
|
local vars var
|
|
|
|
for var in ${!PKG_*}; do
|
|
if [ "${var}" = "PKG_CONFIG" ] || \
|
|
[ "${var}" = "PKG_CONFIG_PATH" ] || \
|
|
[ "${var}" = "PKG_CONFIG_LIBDIR" ] || \
|
|
[ "${var}" = "PKG_CONFIG_SYSROOT_BASE" ] || \
|
|
[ "${var}" = "PKG_CONFIG_SYSROOT_DIR" ] || \
|
|
[ "${var}" = "PKG_CONFIG_ALLOW_SYSTEM_CFLAGS" ] || \
|
|
[ "${var}" = "PKG_CONFIG_ALLOW_SYSTEM_LIBS" ]; then
|
|
continue
|
|
fi
|
|
vars+="${var} "
|
|
done
|
|
[ -n "${vars}" ] && unset -v ${vars}
|
|
|
|
PKG_VERSION="0.0invalid"
|
|
PKG_REV="0"
|
|
PKG_ARCH="any"
|
|
PKG_LICENSE="unknown"
|
|
PKG_TOOLCHAIN="auto"
|
|
PKG_IS_ADDON="no"
|
|
PKG_PYTHON_VERSION="${DEFAULT_PYTHON_VERSION}"
|
|
}
|
|
|
|
set_debug_depends() {
|
|
local pkg dep_pkg map tmp_array mpkg bpkg kvpair
|
|
|
|
_DEBUG_DEPENDS_LIST=""
|
|
_DEBUG_PACKAGE_LIST=""
|
|
if [ "${DEBUG:-no}" != "no" ]; then
|
|
# Convert DEBUG_GROUPS into array of groups, adding "all" if required
|
|
declare -A debug_group_map
|
|
for kvpair in ${DEBUG_GROUPS}; do
|
|
debug_group_map+=(["${kvpair%=*}"]="${kvpair#*=}")
|
|
done
|
|
[ -z "${debug_group_map["all"]}" ] && debug_group_map+=(["all"]="all")
|
|
|
|
# Expand $DEBUG into $_DEBUG_PACKAGE_LIST
|
|
for pkg in ${DEBUG//,/ }; do
|
|
[ "${pkg}" = "yes" ] && pkg="${DEBUG_GROUP_YES:-all}"
|
|
map="${debug_group_map["${pkg}"]}"
|
|
[ -z "${map}" ] && map="${pkg}"
|
|
for mpkg in ${map//,/ }; do
|
|
[[ ${mpkg} =~ ^[!-] ]] && bpkg="${mpkg:1}" || bpkg="${mpkg}"
|
|
[[ ${bpkg} =~ \+$ ]] && bpkg="${bpkg::-1}"
|
|
# Remove existing instances of this package
|
|
listcontains "${_DEBUG_PACKAGE_LIST}" "[!-]?${bpkg}[+]?" && _DEBUG_PACKAGE_LIST="$(listremoveitem "${_DEBUG_PACKAGE_LIST}" "[!-]?${bpkg}[+]?")"
|
|
# Add package
|
|
_DEBUG_PACKAGE_LIST+=" ${mpkg}"
|
|
done
|
|
done
|
|
# Use array word splitting to squash spaces
|
|
tmp_array=(${_DEBUG_PACKAGE_LIST})
|
|
_DEBUG_PACKAGE_LIST="${tmp_array[@]}"
|
|
|
|
# Determine dependencies for each package+
|
|
for pkg in ${_DEBUG_PACKAGE_LIST}; do
|
|
if [ "${pkg}" != "all" ] && [[ ! ${pkg} =~ ^[!-] ]]; then
|
|
! listcontains "${_DEBUG_DEPENDS_LIST}" "${pkg}" && _DEBUG_DEPENDS_LIST+=" ${pkg}"
|
|
[[ ! ${pkg} =~ \+$ ]] && continue
|
|
for dep_pkg in $(get_pkg_variable ${pkg::-1} PKG_DEPENDS_TARGET); do
|
|
[ "${dep_pkg}" = "toolchain" ] && continue
|
|
[[ ${dep_pkg} =~ ^.*:host$ ]] && continue
|
|
! listcontains "${_DEBUG_DEPENDS_LIST}" "${dep_pkg}" && _DEBUG_DEPENDS_LIST+=" ${dep_pkg}"
|
|
done
|
|
fi
|
|
done
|
|
tmp_array=(${_DEBUG_DEPENDS_LIST})
|
|
_DEBUG_DEPENDS_LIST="${tmp_array[@]}"
|
|
fi
|
|
export _DEBUG_DEPENDS_LIST _DEBUG_PACKAGE_LIST
|
|
}
|
|
|
|
# Return 0 if building with debug is enabled for the current package (or all packages).
|
|
# Examples: DEBUG=yes DEBUG=all DEBUG='all,!linux' DEBUG=kodi DEBUG=kodi,samba
|
|
build_with_debug() {
|
|
if [ "${DEBUG:-no}" != "no" -a -n "${PKG_NAME}" -a -n "${_DEBUG_DEPENDS_LIST+x}" ]; then
|
|
# Return 1 if this package is not to be built with debug
|
|
listcontains "${_DEBUG_PACKAGE_LIST}" "[!-]${PKG_NAME}[+]?" && return 1
|
|
|
|
# Build all packages with debug
|
|
listcontains "${_DEBUG_PACKAGE_LIST}" "all" && return 0
|
|
|
|
# Debugging is enabled for at least one package, so enable debug in the "debug" virtual package
|
|
[ "${PKG_NAME}" = "debug" ] && return 0
|
|
|
|
# Build addons with debug if we're building the mediacenter with debug and with dependencies
|
|
[ "${PKG_IS_ADDON}" = "yes" -o "${PKG_IS_ADDON}" = "embedded" ] && listcontains "${_DEBUG_DEPENDS_LIST}" "${MEDIACENTER}\+" && return 0
|
|
|
|
# Build kernel packages with debug if we're building the kernel with debug and with dependencies
|
|
[ "${PKG_IS_KERNEL_PKG}" = "yes" ] && listcontains "${_DEBUG_DEPENDS_LIST}" "linux\+" && return 0
|
|
|
|
# Build this package with debug if it's a resolved dependency
|
|
listcontains "${_DEBUG_DEPENDS_LIST}" "${PKG_NAME}" && return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
# strip
|
|
debug_strip() {
|
|
if [ -z "${BUILD_WITH_DEBUG}" ]; then
|
|
die "ERROR: debug_strip() must not be called without configuring BUILD_WITH_DEBUG"
|
|
fi
|
|
|
|
if [ "${BUILD_WITH_DEBUG}" != "yes" ] && flag_enabled "strip" "yes"; then
|
|
find $* -type f -executable | xargs $STRIP 2>/dev/null || :
|
|
fi
|
|
}
|
|
|
|
init_package_cache() {
|
|
local _ANCHOR="@?+?@"
|
|
local temp_global temp_local
|
|
|
|
# If the package caches are unset, then populate them
|
|
if [ -z "${_CACHE_PACKAGE_LOCAL}" -o -z "${_CACHE_PACKAGE_GLOBAL}" ]; then
|
|
temp_global="$(mktemp)"
|
|
temp_local="$(mktemp)"
|
|
|
|
# cache project/device folder for packages
|
|
if [ -n "${DEVICE}" ]; then
|
|
find "${ROOT}/projects/${PROJECT}/devices/${DEVICE}/packages" -type f -name package.mk 2>/dev/null | sed "s#/package\.mk\$#${_ANCHOR}#" >> "${temp_local}"
|
|
fi
|
|
|
|
# cache project folder for packages
|
|
find "${ROOT}/projects/${PROJECT}/packages" -type f -name package.mk 2>/dev/null | sed "s#/package\.mk\$#${_ANCHOR}#" >> "${temp_local}"
|
|
|
|
# cache packages folder
|
|
find "${ROOT}/${PACKAGES}" -type f -name package.mk 2>/dev/null | sed "s#/package\.mk\$#${_ANCHOR}#" >> "${temp_global}"
|
|
|
|
_CACHE_PACKAGE_LOCAL="${BUILD}/.cache_package_local"
|
|
_CACHE_PACKAGE_GLOBAL="${BUILD}/.cache_package_global"
|
|
export _CACHE_PACKAGE_LOCAL _CACHE_PACKAGE_GLOBAL
|
|
|
|
# overwrite existing cache files only when they are invalid, or not yet created
|
|
mkdir -p "${_CACHE_PACKAGE_GLOBAL%/*}"
|
|
if [ -f "${_CACHE_PACKAGE_LOCAL}" ] && cmp -s "${temp_local}" "${_CACHE_PACKAGE_LOCAL}"; then
|
|
rm "${temp_local}"
|
|
else
|
|
mv "${temp_local}" "${_CACHE_PACKAGE_LOCAL}"
|
|
fi
|
|
if [ -f "${_CACHE_PACKAGE_GLOBAL}" ] && cmp -s "${temp_global}" "${_CACHE_PACKAGE_GLOBAL}"; then
|
|
rm "${temp_global}"
|
|
else
|
|
mv "${temp_global}" "${_CACHE_PACKAGE_GLOBAL}"
|
|
fi
|
|
fi
|
|
|
|
if [ -z "${_DEBUG_DEPENDS_LIST+x}" ]; then
|
|
set_debug_depends
|
|
fi
|
|
}
|
|
|
|
load_build_config() {
|
|
if [ -d "${1}" -a -f ${1}/.build.conf ]; then
|
|
source ${1}/.build.conf
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
save_build_config() {
|
|
local var
|
|
mkdir -p ${BUILD}
|
|
rm -f ${BUILD}/.build.conf
|
|
for var in PROJECT DEVICE ARCH DEBUG BUILD_SUFFIX; do
|
|
echo "export ${var}=\"${!var}\"" >> ${BUILD}/.build.conf
|
|
done
|
|
}
|
|
|
|
check_path() {
|
|
local dashes="===========================" path_err_msg
|
|
if [ "${PWD##/usr}" != "${PWD}" ]; then
|
|
path_err_msg="\n ${dashes}${dashes}${dashes}"
|
|
path_err_msg+="\n ERROR: Detected building inside /usr"
|
|
path_err_msg+="\n ${dashes}${dashes}${dashes}"
|
|
path_err_msg+="\n This is not supported by the buildsystem."
|
|
path_err_msg+="\n Please use another directory (for example your \$HOME) to build ${DISTRONAME}"
|
|
|
|
die "${path_err_msg}"
|
|
fi
|
|
}
|
|
|
|
check_distro() {
|
|
local dashes="===========================" distro_err_msg
|
|
if [ -z "${DISTRO}" -o ! -d "${DISTRO_DIR}/${DISTRO}" ]; then
|
|
distro_err_msg="\n ${dashes}${dashes}${dashes}"
|
|
distro_err_msg+="\n ERROR: Distro not found, use a valid distro or create a new config"
|
|
distro_err_msg+="\n ${dashes}${dashes}${dashes}"
|
|
distro_err_msg+="\n\n Valid distros:"
|
|
|
|
for distros in ${DISTRO_DIR}/*; do
|
|
distro_err_msg+="\n - ${distros##*/}"
|
|
done
|
|
die "${distro_err_msg}"
|
|
fi
|
|
}
|
|
|
|
check_project() {
|
|
local dashes="===========================" project_err_msg
|
|
if [ -z "${PROJECT}" -o ! -d "${PROJECT_DIR}/${PROJECT}" ]; then
|
|
project_err_msg="\n ${dashes}${dashes}${dashes}"
|
|
project_err_msg+="\n ERROR: Project not found. Use a valid project or create a new config"
|
|
project_err_msg+="\n ${dashes}${dashes}${dashes}"
|
|
project_err_msg+="\n\n Valid projects:"
|
|
|
|
for projects in ${PROJECT_DIR}/*; do
|
|
project_err_msg+="\n - ${projects##*/}"
|
|
done
|
|
die "${project_err_msg}"
|
|
fi
|
|
}
|
|
|
|
check_device() {
|
|
local dashes="===========================" device_err_msg
|
|
if [ \( -z "${DEVICE}" -a -d "${PROJECT_DIR}/${PROJECT}/devices" \) -o \
|
|
\( -n "${DEVICE}" -a ! -d "${PROJECT_DIR}/${PROJECT}/devices/${DEVICE}" \) ]; then
|
|
device_err_msg="\n ${dashes}${dashes}${dashes}"
|
|
device_err_msg+="\n ERROR: Specify a valid device for the ${PROJECT} project"
|
|
device_err_msg+="\n ${dashes}${dashes}${dashes}"
|
|
device_err_msg+="\n\n Valid devices for project: ${PROJECT}"
|
|
|
|
for device in ${PROJECT_DIR}/${PROJECT}/devices/*; do
|
|
device_err_msg+="\n - ${device##*/}"
|
|
done
|
|
die "${device_err_msg}"
|
|
fi
|
|
}
|
|
|
|
check_arch() {
|
|
local dashes="===========================" arch_err_msg linux_config_dir
|
|
if [ -d "${PROJECT_DIR}/${PROJECT}/devices/${DEVICE}/linux" ]; then
|
|
linux_config_dir="${PROJECT_DIR}/${PROJECT}/devices/$DEVICE/linux"
|
|
else
|
|
linux_config_dir="${PROJECT_DIR}/${PROJECT}/linux"
|
|
fi
|
|
|
|
if [ ! -e "${linux_config_dir}/linux.${TARGET_KERNEL_PATCH_ARCH:-$TARGET_ARCH}.conf" ] &&
|
|
! ls "${linux_config_dir}/"*/linux.${TARGET_KERNEL_PATCH_ARCH:-$TARGET_ARCH}.conf &>/dev/null; then
|
|
arch_err_msg="\n ${dashes}${dashes}${dashes}"
|
|
arch_err_msg+="\n ERROR: Architecture not found. Use a valid Architecture"
|
|
arch_err_msg+="\n for your project or create a new config"
|
|
arch_err_msg+="\n ${dashes}${dashes}${dashes}"
|
|
arch_err_msg+="\n\n Valid Architectures for project: ${PROJECT}"
|
|
|
|
for arch in ${linux_config_dir}/*.conf ${linux_config_dir}/*/linux.${TARGET_ARCH}.conf; do
|
|
[[ ${arch} =~ .*\*.* ]] && continue #ignore unexpanded wildcard
|
|
arch_err_msg+="\n - $(echo ${arch##*/} | cut -f2 -d".")"
|
|
done
|
|
die "${arch_err_msg}"
|
|
fi
|
|
}
|
|
|
|
check_config() {
|
|
check_path
|
|
check_distro
|
|
check_project
|
|
check_device
|
|
check_arch
|
|
}
|
|
|
|
do_autoreconf() {
|
|
export ACLOCAL_DIR=$SYSROOT_PREFIX/usr/share/aclocal
|
|
|
|
if [ -e "$TOOLCHAIN/bin/autoconf" ]; then
|
|
export AUTOCONF=$TOOLCHAIN/bin/autoconf
|
|
fi
|
|
|
|
if [ -e "$TOOLCHAIN/bin/automake" ]; then
|
|
export AUTOMAKE=$TOOLCHAIN/bin/automake
|
|
fi
|
|
|
|
if [ -e "$TOOLCHAIN/bin/autopoint" ]; then
|
|
export AUTOPOINT=$TOOLCHAIN/bin/autopoint
|
|
fi
|
|
|
|
if [ -e "$TOOLCHAIN/bin/libtoolize" ]; then
|
|
export LIBTOOLIZE=$TOOLCHAIN/bin/libtoolize
|
|
fi
|
|
|
|
if [ -e "$TOOLCHAIN/bin/intltoolize" ]; then
|
|
export INTLTOOLIZE=$TOOLCHAIN/bin/intltoolize
|
|
fi
|
|
|
|
if [ -e "$TOOLCHAIN/bin/aclocal" ]; then
|
|
export ACLOCAL="$TOOLCHAIN/bin/aclocal -I $ACLOCAL_DIR"
|
|
fi
|
|
|
|
if [ -e "$TOOLCHAIN/bin/autoheader" ]; then
|
|
export AUTOHEADER=$TOOLCHAIN/bin/autoheader
|
|
fi
|
|
|
|
if [ -e "$TOOLCHAIN/bin/libtool" ]; then
|
|
export LIBTOOL=$TOOLCHAIN/bin/libtool
|
|
fi
|
|
|
|
# >autoconf-2.69 will call gtkdocize when used in macros
|
|
# when called with --install parameter.
|
|
# use "true" unless gtkdocsize is in the toolchain.
|
|
if [ -e "$TOOLCHAIN/bin/gtkdocize" ]; then
|
|
export GTKDOCIZE=$TOOLCHAIN/bin/gtkdocize
|
|
else
|
|
export GTKDOCIZE=true
|
|
fi
|
|
|
|
if [ -e "$TOOLCHAIN/bin/autoreconf" -a -e "$INTLTOOLIZE" ]; then
|
|
mkdir -p $ACLOCAL_DIR
|
|
if [ -e "$LIBTOOLIZE" ]; then
|
|
export AUTORECONF="$TOOLCHAIN/bin/autoreconf --verbose --force --install -I $ACLOCAL_DIR"
|
|
else
|
|
export AUTORECONF="$TOOLCHAIN/bin/autoreconf --verbose --force -I $ACLOCAL_DIR"
|
|
fi
|
|
$AUTORECONF $@
|
|
fi
|
|
}
|
|
|
|
# True if this is a sequential build, false if multithreaded
|
|
is_sequential_build() {
|
|
[ "${MTWITHLOCKS}" != "yes" ] && return 0 || return 1
|
|
}
|
|
|
|
# arg1: filename (libtool) to remove hardcode rpath when --disable-rpath is not supported by configure
|
|
libtool_remove_rpath() {
|
|
sed -i 's|^hardcode_libdir_flag_spec=.*|hardcode_libdir_flag_spec=""|g' ${1}
|
|
sed -i 's|^runpath_var=LD_RUN_PATH|runpath_var=DIE_RPATH_DIE|g' ${1}
|
|
}
|
|
|
|
### PACKAGE HELPERS ###
|
|
# get variable ($2) for package ($1).
|
|
# avoid infinite recursion if required package is already loaded.
|
|
get_pkg_variable() {
|
|
if [ -n "$1" -a -n "$2" ] ; then
|
|
if [ "$1" != "$PKG_NAME" ]; then
|
|
source_package "${1}"
|
|
fi
|
|
echo "${!2}"
|
|
fi
|
|
}
|
|
|
|
# get package's build dir
|
|
get_build_dir() {
|
|
local _PKG_NAME="${1%:*}" _PKG_VERSION="$(get_pkg_version "$1")"
|
|
if [ -n "$_PKG_NAME" -a -n "$_PKG_VERSION" ]; then
|
|
echo $BUILD/build/${_PKG_NAME}-${_PKG_VERSION}
|
|
fi
|
|
}
|
|
|
|
get_install_dir() {
|
|
get_pkg_variable "$1" PKG_INSTALL
|
|
}
|
|
|
|
get_pkg_version() {
|
|
get_pkg_variable "$1" PKG_VERSION
|
|
}
|
|
|
|
get_pkg_version_maj_min() {
|
|
local pkg_version
|
|
|
|
[ -n "${1}" ] && pkg_version="$(get_pkg_version "${1}")" || pkg_version="${PKG_VERSION}"
|
|
|
|
if [[ ${pkg_version} =~ ^[0-9A-Za-z]*\.[0-9A-Za-z]*\.[0-9A-za-z]*$ ]]; then
|
|
echo "${pkg_version%.*}"
|
|
elif [[ ${pkg_version} =~ ^[0-9A-Za-z]*\.[0-9A-Za-z]*$ ]]; then
|
|
echo "${pkg_version}"
|
|
else
|
|
echo "${pkg_version}"
|
|
fi
|
|
}
|
|
|
|
get_pkg_directory() {
|
|
local _PKG_ROOT_NAME=${1%:*} _ALL_DIRS _FOUND=0 _ANCHOR="@?+?@" _PKG_DIR _DIR
|
|
|
|
# Check for any available local package in preference to a global package
|
|
for _DIR in $(grep -F "/${_PKG_ROOT_NAME}${_ANCHOR}" "${_CACHE_PACKAGE_LOCAL}"); do
|
|
_DIR="${_DIR%${_ANCHOR}}"
|
|
# found first, set $_PKG_DIR
|
|
_PKG_DIR="$_DIR"
|
|
# keep track of dirs with package.mk for detecting multiple folders
|
|
_ALL_DIRS+="${_DIR}\n"
|
|
_FOUND=$((_FOUND+1))
|
|
done
|
|
|
|
# If there's no local package available, use the global package
|
|
if [ $_FOUND -eq 0 ]; then
|
|
for _DIR in $(grep -F "/${_PKG_ROOT_NAME}${_ANCHOR}" "${_CACHE_PACKAGE_GLOBAL}"); do
|
|
_DIR="${_DIR%${_ANCHOR}}"
|
|
# found first, set $_PKG_DIR
|
|
_PKG_DIR="$_DIR"
|
|
# keep track of dirs with package.mk for detecting multiple folders
|
|
_ALL_DIRS+="${_DIR}\n"
|
|
_FOUND=$((_FOUND+1))
|
|
done
|
|
fi
|
|
|
|
# _FOUND multiple packages? fail
|
|
if [ $_FOUND -gt 1 ]; then
|
|
echo "Error - multiple package folders for package ${_PKG_ROOT_NAME}:" >&2
|
|
echo -e "$_ALL_DIRS" >&2
|
|
die
|
|
fi
|
|
|
|
echo "$_PKG_DIR"
|
|
}
|
|
|
|
# Return a list of sorted package names for this project/device/arch
|
|
get_all_package_names() {
|
|
sed -e 's#@?+?@##g; s#.*/##g' ${_CACHE_PACKAGE_GLOBAL} ${_CACHE_PACKAGE_LOCAL} | sort --ignore-case --unique
|
|
}
|
|
|
|
calculate_stamp() {
|
|
local stamp data
|
|
|
|
stamp="$PKG_DIR $PROJECT_DIR/$PROJECT/patches/$PKG_NAME $PROJECT_DIR/$PROJECT/packages/$PKG_NAME"
|
|
[ -n "$DEVICE" ] && stamp+=" $PROJECT_DIR/$PROJECT/devices/$DEVICE/patches/$PKG_NAME"
|
|
[ -n "$PKG_NEED_UNPACK" ] && stamp+=" $PKG_NEED_UNPACK"
|
|
|
|
data="$(find -L ${stamp} -type f -not -name '.*' 2>/dev/null | sed "s|^${ROOT}/||" | LC_ALL=C sort -u | xargs sha256sum)"
|
|
[ -n "${PKG_STAMP}" ] && data+=$'\n'"$(echo "${PKG_STAMP}" | sha256sum)"
|
|
|
|
echo "${data}" | sha256sum | cut -d" " -f1
|
|
}
|
|
|
|
target_has_feature() {
|
|
listcontains "$TARGET_FEATURES" "$1"
|
|
}
|
|
|
|
# configure variables for go
|
|
go_configure() {
|
|
unset GOARCH GOARM
|
|
case ${TARGET_ARCH} in
|
|
x86_64)
|
|
export GOARCH=amd64
|
|
;;
|
|
arm)
|
|
export GOARCH=arm
|
|
|
|
case ${TARGET_CPU} in
|
|
arm1176jzf-s)
|
|
export GOARM=6
|
|
;;
|
|
*)
|
|
export GOARM=7
|
|
;;
|
|
esac
|
|
;;
|
|
aarch64)
|
|
export GOARCH=arm64
|
|
;;
|
|
esac
|
|
|
|
export GOOS=linux
|
|
export GOROOT=${TOOLCHAIN}/lib/golang
|
|
export PATH=${PATH}:${GOROOT}/bin
|
|
|
|
go_configure_path
|
|
|
|
export CGO_ENABLED=1
|
|
export CGO_NO_EMULATION=1
|
|
export CGO_CFLAGS=$CFLAGS
|
|
}
|
|
|
|
go_configure_path() {
|
|
export GOLANG=${TOOLCHAIN}/lib/golang/bin/go
|
|
export GOPATH=${PKG_BUILD}/.gopath
|
|
export GOFLAGS="-modcacherw"
|
|
}
|
|
|
|
# find path for matching file or directory, searching standard directory hierarchy, using optional default
|
|
# if a path is located it will be set in FOUND_PATH and exit code will be 0.
|
|
find_path() {
|
|
local test_func="$1" search="$2" default="$3"
|
|
local dir match wildcard=0 ftype
|
|
|
|
# support wildcard matches
|
|
[[ $search =~ \* || $search =~ \? ]] && wildcard=1
|
|
|
|
[ "$test_func" = "-f" ] && ftype="file" || ftype="dir"
|
|
|
|
for dir in $PROJECT_DIR/$PROJECT/devices/$DEVICE/packages/$PKG_NAME \
|
|
$PROJECT_DIR/$PROJECT/devices/$DEVICE \
|
|
$PROJECT_DIR/$PROJECT/packages/$PKG_NAME \
|
|
$PROJECT_DIR/$PROJECT \
|
|
$DISTRO_DIR/$DISTRO/packages/$PKG_NAME \
|
|
$DISTRO_DIR/$DISTRO \
|
|
$PKG_DIR \
|
|
; do
|
|
# ignore directories with missing DEVICE or PKG_NAME components
|
|
[[ $dir =~ /packages/$ ]] && continue
|
|
[[ $dir =~ /devices/$ ]] && continue
|
|
[[ $dir =~ /devices//packages/$PKG_NAME$ ]] && continue
|
|
|
|
if [ $wildcard -eq 1 ]; then
|
|
ls $dir/$search 1>/dev/null 2>&1 && match="$dir/$search" && break
|
|
else
|
|
[ $test_func "$dir/$search" ] && match="$dir/$search" && break
|
|
fi
|
|
done
|
|
|
|
if [ -z "$match" -a -n "$default" ]; then
|
|
if [[ $default =~ \* || $default =~ \? ]]; then
|
|
ls $default 1>/dev/null 2>&1 && match="$default"
|
|
else
|
|
[ $test_func "$default" ] && match="$default"
|
|
fi
|
|
fi
|
|
|
|
if [ -n "$match" ]; then
|
|
FOUND_PATH="$match"
|
|
[ "${VERBOSE_FIND_PATH,,}" = "yes" ] && echo "find_path: Searching for $ftype: \"$search\", found: \"$FOUND_PATH\"" >&2
|
|
return 0
|
|
else
|
|
unset FOUND_PATH
|
|
[ "${VERBOSE_FIND_PATH,,}" = "yes" ] && echo "find_path: Searching for $ftype: \"$search\" - not found" >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
find_file_path() {
|
|
find_path -f "$1" "$2"
|
|
}
|
|
|
|
find_dir_path() {
|
|
find_path -d "$1" "$2"
|
|
}
|
|
|
|
# p1: name of function to test for
|
|
# return 0 if function exists, 1 if not
|
|
pkg_call_exists() {
|
|
PKG_CURRENT_CALL="${1}"
|
|
if [ "$(type -t ${1})" = "function" ]; then
|
|
PKG_CURRENT_CALL_TYPE="package.mk"
|
|
return 0
|
|
else
|
|
PKG_CURRENT_CALL_TYPE="default"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Optional variant of pkg_call_exists()
|
|
# Clear PKG_CURRENT_CALL when function is not implemented.
|
|
pkg_call_exists_opt() {
|
|
if pkg_call_exists $1; then
|
|
return 0
|
|
else
|
|
pkg_call_finish
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Function to be called is set by pkg_call_exists/pkg_call_exists_opt
|
|
# Args: whatever the called function expects
|
|
# testing the exit code value of this function is likely to break set -e fail-on-error behaviour
|
|
pkg_call() {
|
|
[ -n "${PKG_CURRENT_CALL}" ] || die "$(print_color CLR_ERROR "PKG_CURRENT_CALL is not set!")"
|
|
[ -n "${PKG_NAME}" ] || die "$(print_color CLR_ERROR "FAILURE: Cannot call ${PKG_CURRENT_CALL} package function when package is not known!")"
|
|
|
|
${PKG_CURRENT_CALL} "${@}"
|
|
pkg_call_finish
|
|
}
|
|
|
|
pkg_call_finish() {
|
|
PKG_CURRENT_CALL=""
|
|
}
|
|
|
|
unset_functions() {
|
|
local target
|
|
|
|
unset -f configure_package
|
|
|
|
unset -f pre_unpack unpack post_unpack
|
|
unset -f pre_patch post_patch
|
|
|
|
for target in target host init bootstrap; do
|
|
unset -f pre_build_${target}
|
|
unset -f pre_configure_${target} configure_${target} post_configure_${target}
|
|
unset -f pre_make_${target} make_${target} post_make_${target}
|
|
unset -f pre_makeinstall_${target} makeinstall_${target} post_makeinstall_${target}
|
|
done
|
|
|
|
unset -f pre_install post_install
|
|
|
|
unset -f addon post_install_addon
|
|
}
|
|
|
|
# p1: name of package to be sourced
|
|
source_package() {
|
|
local opwd="${PWD}"
|
|
|
|
# Don't use BUILD_WITH_DEBUG in "global" package.mk - instead, call the function
|
|
# build_with_debug() directly as the function depends on various package.mk
|
|
# variables that will be in the process of being configured. Once package.mk is
|
|
# fully sourced we can set this variable and use it in situations where we know the
|
|
# package has already been sourced.
|
|
unset BUILD_WITH_DEBUG
|
|
|
|
reset_pkg_vars
|
|
unset_functions
|
|
|
|
if [ -n "${1}" ]; then
|
|
[ -f "${1}" ] && PKG_DIR="${1%/*}" || PKG_DIR="$(get_pkg_directory "${1}")"
|
|
|
|
[ -n "$PKG_DIR" -a -r $PKG_DIR/package.mk ] || die "FAILURE: unable to source package - ${1}/package.mk does not exist"
|
|
|
|
cd "${ROOT}"
|
|
. ${PKG_DIR}/package.mk || die "FAILURE: an error occurred while sourcing ${PKG_DIR}/package.mk"
|
|
cd "${opwd}"
|
|
|
|
PKG_SHORTDESC="${PKG_SHORTDESC:-${PKG_NAME} (autogenerated)}"
|
|
PKG_LONGDESC="${PKG_LONGDESC:-${PKG_NAME} (autogenerated)}"
|
|
|
|
if [ "$PKG_IS_ADDON" = "yes" -o "$PKG_IS_ADDON" = "embedded" ] ; then
|
|
[ -z $PKG_SECTION ] && PKG_ADDON_ID="$PKG_NAME" || PKG_ADDON_ID="${PKG_SECTION//\//.}.$PKG_NAME"
|
|
[ "$PKG_ADDON_IS_STANDALONE" != "yes" ] && PKG_NEED_UNPACK="${PKG_NEED_UNPACK} $(get_pkg_directory $MEDIACENTER)"
|
|
fi
|
|
|
|
if [ -n "${PKG_IS_KERNEL_PKG}" -a "${PKG_NAME}" != "linux" ]; then
|
|
PKG_DEPENDS_TARGET="toolchain linux ${PKG_DEPENDS_TARGET}"
|
|
PKG_DEPENDS_UNPACK="linux ${PKG_DEPENDS_UNPACK}"
|
|
PKG_NEED_UNPACK="${LINUX_DEPENDS} ${PKG_NEED_UNPACK}"
|
|
fi
|
|
|
|
if [ -n "${PKG_DEPENDS_UNPACK}" ]; then
|
|
for _p in ${PKG_DEPENDS_UNPACK}; do
|
|
PKG_NEED_UNPACK+=" $(get_pkg_directory ${_p})"
|
|
done
|
|
fi
|
|
|
|
# Automatically set PKG_SOURCE_NAME unless it is already defined.
|
|
# PKG_SOURCE_NAME will be automatically set to a name based on
|
|
# the $PKG_NAME-$PKG_VERSION convention.
|
|
#
|
|
# Any $PKG_URL that references more than a single url will abort
|
|
# the build as these are no longer supported - use mkpkg instead.
|
|
if [ -n "$PKG_URL" -a -z "$PKG_SOURCE_NAME" ]; then
|
|
if [[ $PKG_URL =~ .*\ .* ]]; then
|
|
echo "Error - packages with multiple urls are no longer supported, use mkpkg."
|
|
echo "$PKG_URL"
|
|
die
|
|
fi
|
|
if [[ ${PKG_URL} =~ .git$ || ${PKG_URL} =~ ^git:// ]]; then
|
|
PKG_SOURCE_NAME=${PKG_NAME}-${PKG_VERSION}
|
|
elif [[ ${PKG_URL} =~ ^file:// ]]; then
|
|
PKG_SOURCE_NAME=${PKG_URL#file://}
|
|
# if no specific PKG_TAR_COPY_OPTS then default to excluding .git and .svn as they can be huge
|
|
[ -z "${PKG_TAR_COPY_OPTS+x}" ] && PKG_TAR_COPY_OPTS="--exclude=.git --exclude=.svn"
|
|
else
|
|
PKG_SOURCE_NAME="${PKG_URL##*/}"
|
|
case $PKG_SOURCE_NAME in
|
|
${PKG_NAME}-${PKG_VERSION}.*)
|
|
PKG_SOURCE_NAME=$PKG_SOURCE_NAME
|
|
;;
|
|
*.tar | *.tbz | *.tgz | *.txz | *.tzst | *.7z | *.zip)
|
|
PKG_SOURCE_NAME=${PKG_NAME}-${PKG_VERSION}.${PKG_SOURCE_NAME##*\.}
|
|
;;
|
|
*.tar.bz2 | *.tar.gz | *.tar.xz | *.tar.zst )
|
|
PKG_SOURCE_NAME=${PKG_NAME}-${PKG_VERSION}.tar.${PKG_SOURCE_NAME##*\.}
|
|
;;
|
|
*.diff | *.patch | *.diff.bz2 | *.patch.bz2 | patch-*.bz2 | *.diff.gz | *.patch.gz | patch-*.gz)
|
|
PKG_SOURCE_NAME=$PKG_SOURCE_NAME
|
|
;;
|
|
*)
|
|
PKG_SOURCE_NAME=${PKG_NAME}-${PKG_VERSION}.${PKG_SOURCE_NAME##*\.}
|
|
;;
|
|
esac
|
|
fi
|
|
fi
|
|
|
|
PKG_BUILD="$BUILD/build/${PKG_NAME}-${PKG_VERSION}"
|
|
|
|
if [[ "${1}" =~ :target$ || "${1//:/}" = "${1}" ]]; then
|
|
PKG_INSTALL="$BUILD/install_pkg/${PKG_NAME}-${PKG_VERSION}"
|
|
elif [[ "${1}" =~ :init$ ]]; then
|
|
PKG_INSTALL="$BUILD/install_init/${PKG_NAME}-${PKG_VERSION}"
|
|
fi
|
|
fi
|
|
|
|
build_with_debug && BUILD_WITH_DEBUG="yes" || BUILD_WITH_DEBUG="no"
|
|
|
|
# Late variable binding - allow the package to now evaluate any variables
|
|
# that we may have initialised after sourcing the package, typically
|
|
# PKG_BUILD etc.
|
|
if [ -n "${PKG_NAME}" ]; then
|
|
if pkg_call_exists_opt configure_package; then
|
|
pkg_call
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# arg1: file, or directory to recursively compile.
|
|
python_compile() {
|
|
local path="${1:-${INSTALL}/usr/lib/${PKG_PYTHON_VERSION}}"
|
|
${TOOLCHAIN}/bin/python3 -Wi -t -B ${TOOLCHAIN}/lib/${PKG_PYTHON_VERSION}/compileall.py -f -d "${path#${INSTALL}}" "${path}"
|
|
python_remove_source "${path}"
|
|
}
|
|
|
|
# arg1: file, or directory from which to recursively remove all py source code
|
|
python_remove_source() {
|
|
local path="${1:-${INSTALL}/usr/lib/${PKG_PYTHON_VERSION}}"
|
|
if [ -d "${path}" ]; then
|
|
find "${path}" -type f -name '*.py' -delete
|
|
else
|
|
rm -f "${path}"
|
|
fi
|
|
}
|
|
|
|
# arg1: directory to process recursively
|
|
# strip incorrect build-host ABI from native Python3 modules (see PEP3149)
|
|
python_fix_abi() {
|
|
local pymodule pyname
|
|
|
|
for pymodule in $(find ${1} -type f -name '*.cpython-*.so' 2>/dev/null); do
|
|
pyname=${pymodule##*/}
|
|
pyname=${pyname%.so} # strip extension
|
|
pyname=${pyname%.*} # strip incorrect ABI
|
|
echo "python_fix_abi: Removing ABI from ${pymodule} -> ${pyname}.so"
|
|
mv ${pymodule} ${pymodule%/*}/${pyname}.so
|
|
done
|
|
}
|
|
|
|
### KERNEL HELPERS ###
|
|
kernel_path() {
|
|
get_build_dir linux
|
|
}
|
|
|
|
kernel_version() {
|
|
get_pkg_version linux
|
|
}
|
|
|
|
kernel_config_path() {
|
|
local cfg pkg_linux_dir pkg_linux_version config_name
|
|
|
|
pkg_linux_version="$(get_pkg_version linux)"
|
|
pkg_linux_dir="$(get_pkg_directory linux)"
|
|
|
|
config_name="linux.${TARGET_KERNEL_PATCH_ARCH:-$TARGET_ARCH}.conf"
|
|
|
|
for cfg in $PROJECT_DIR/$PROJECT/devices/$DEVICE/linux/$pkg_linux_version/$config_name \
|
|
$PROJECT_DIR/$PROJECT/devices/$DEVICE/linux/$LINUX/$config_name \
|
|
$PROJECT_DIR/$PROJECT/devices/$DEVICE/linux/$config_name \
|
|
$PROJECT_DIR/$PROJECT/linux/$pkg_linux_version/$config_name \
|
|
$PROJECT_DIR/$PROJECT/linux/$LINUX/$config_name \
|
|
$PROJECT_DIR/$PROJECT/linux/$config_name \
|
|
$pkg_linux_dir/config/$pkg_linux_version/$config_name \
|
|
$pkg_linux_dir/config/$LINUX/$config_name \
|
|
$pkg_linux_dir/config/$config_name \
|
|
; do
|
|
[[ $cfg =~ /devices//linux/ ]] && continue
|
|
[ -f "$cfg" ] && echo "$cfg" && return
|
|
done
|
|
|
|
die "ERROR: Unable to locate kernel config for ${LINUX} - looking for ${config_name}"
|
|
}
|
|
|
|
kernel_initramfs_confs() {
|
|
local config_name cfg confs
|
|
|
|
config_name="initramfs.${TARGET_KERNEL_PATCH_ARCH:-$TARGET_ARCH}.conf"
|
|
confs="$(get_pkg_directory initramfs)/config/initramfs.conf"
|
|
|
|
for cfg in $PROJECT_DIR/$PROJECT/packages/initramfs/config/$config_name \
|
|
$PROJECT_DIR/$PROJECT/devices/$DEVICE/packages/initramfs/config/$config_name \
|
|
; do
|
|
[[ $cfg =~ /devices//packages/ ]] && continue
|
|
[ -f "$cfg" ] && confs+=" $cfg"
|
|
done
|
|
|
|
echo "$confs"
|
|
}
|
|
|
|
kernel_make() {
|
|
(
|
|
setup_pkg_config_host
|
|
|
|
LDFLAGS="" make CROSS_COMPILE=$TARGET_KERNEL_PREFIX \
|
|
ARCH="$TARGET_KERNEL_ARCH" \
|
|
HOSTCC="$TOOLCHAIN/bin/host-gcc" \
|
|
HOSTCXX="$TOOLCHAIN/bin/host-g++" \
|
|
HOSTCFLAGS="$HOST_CFLAGS" \
|
|
HOSTLDFLAGS="$HOST_LDFLAGS" \
|
|
HOSTCXXFLAGS="$HOST_CXXFLAGS" \
|
|
DEPMOD="$TOOLCHAIN/bin/depmod" \
|
|
"$@"
|
|
)
|
|
}
|
|
|
|
# get kernel module dir
|
|
get_module_dir() {
|
|
if [ -n "${_CACHED_KERNEL_MODULE_DIR}" ]; then
|
|
echo "${_CACHED_KERNEL_MODULE_DIR}"
|
|
else
|
|
basename $(ls -d $(get_install_dir linux)/usr/lib/kernel-overlays/base/lib/modules/*)
|
|
fi
|
|
}
|
|
|
|
# get base path to kernel modules and firmware
|
|
get_kernel_overlay_dir() {
|
|
echo "usr/lib/kernel-overlays/${1:-base}"
|
|
}
|
|
|
|
# get full path to kernel module dir
|
|
# optional parameter specifies overlay level (default is base)
|
|
get_full_module_dir() {
|
|
echo "$(get_kernel_overlay_dir $1)/lib/modules/$(get_module_dir)"
|
|
}
|
|
|
|
# get full path to firmware dir
|
|
# optional parameter specifies overlay level (default is base)
|
|
get_full_firmware_dir() {
|
|
echo "$(get_kernel_overlay_dir $1)/lib/firmware"
|
|
}
|
|
|
|
|
|
### ADDON HELPERS ###
|
|
install_binary_addon() {
|
|
local addon_id="$1" addon_so
|
|
|
|
mkdir -p $ADDON_BUILD/$addon_id/
|
|
cp -R $PKG_INSTALL/usr/share/$MEDIACENTER/addons/$addon_id/* $ADDON_BUILD/$addon_id/
|
|
|
|
addon_so=$(xmlstarlet sel -t -v "/addon/extension/@library_linux" $ADDON_BUILD/$addon_id/addon.xml || :)
|
|
if [ -n "$addon_so" ]; then
|
|
cp -L $PKG_INSTALL/usr/lib/$MEDIACENTER/addons/$addon_id/$addon_so $ADDON_BUILD/$addon_id/
|
|
chmod +x $ADDON_BUILD/$addon_id/$addon_so
|
|
fi
|
|
|
|
if [ -d $PKG_INSTALL/usr/lib/kernel-overlays/$addon_id ] ; then
|
|
mkdir -p $ADDON_BUILD/$addon_id/kernel-overlay
|
|
cp -PR $PKG_INSTALL/usr/lib/kernel-overlays/$addon_id/* $ADDON_BUILD/$addon_id/kernel-overlay
|
|
fi
|
|
}
|
|
|
|
install_addon_source() {
|
|
if [ -d $PKG_DIR/source ]; then
|
|
cp -R $PKG_DIR/source/* "$1"
|
|
fi
|
|
}
|
|
|
|
install_addon_images() {
|
|
local dest_dir="$1"
|
|
|
|
if [ -f "$PKG_DIR/icon/icon.png" ]; then
|
|
mkdir -p "$dest_dir/resources"
|
|
cp "$PKG_DIR/icon/icon.png" "$dest_dir/resources"
|
|
fi
|
|
|
|
if [ -f "$DISTRO_DIR/$DISTRO/addons/fanart.png" ]; then
|
|
mkdir -p "$dest_dir/resources"
|
|
cp "$DISTRO_DIR/$DISTRO/addons/fanart.png" "$dest_dir/resources"
|
|
fi
|
|
}
|
|
|
|
create_addon_xml() {
|
|
local addon_xml addon_version addon_name provider_name requires requires_addonname requires_addonversion screenshots
|
|
local tmp_changelog
|
|
|
|
addon_xml="$1/addon.xml"
|
|
|
|
IFS=" "
|
|
for i in $PKG_ADDON_REQUIRES; do
|
|
requires_addonname=`echo $i | cut -f1 -d ":"`
|
|
requires_addonversion=`echo $i | cut -f2 -d ":"`
|
|
requires="$requires\n <import addon=\"$requires_addonname\" version=\"$requires_addonversion\" />"
|
|
done
|
|
unset IFS
|
|
|
|
if [ ! -f "$addon_xml" ] ; then
|
|
cp $ROOT/config/addon/${PKG_ADDON_TYPE}.xml "$addon_xml"
|
|
addon_version=${PKG_ADDON_VERSION:-${ADDON_VERSION}.${PKG_REV}}
|
|
else
|
|
if ! command -v xmlstarlet >/dev/null ; then
|
|
die "*** ERROR: $ADDON has addon.xml shipped, you need 'xmlstarlet' ***" "255"
|
|
fi
|
|
addon_version="${PKG_ADDON_VERSION:-$(xmlstarlet sel -t -v "/addon/@version" "$addon_xml").$PKG_REV}"
|
|
xmlstarlet ed --inplace -u "/addon[@version]/@version" -v "$addon_version" "$addon_xml"
|
|
fi
|
|
|
|
if [ -f $PKG_DIR/changelog.txt ]; then
|
|
tmp_changelog="$(mktemp)"
|
|
cat ${PKG_DIR}/changelog.txt | xmlstarlet esc >"${tmp_changelog}"
|
|
sed -e "/@PKG_ADDON_NEWS@/ \
|
|
{
|
|
r ${tmp_changelog}
|
|
d
|
|
}" -i "$addon_xml"
|
|
rm -f "${tmp_changelog}"
|
|
else
|
|
sed -e "s|@PKG_ADDON_NEWS@||g" -i "$addon_xml"
|
|
fi
|
|
|
|
provider_name=${PKG_MAINTAINER:-"Team ${DISTRONAME}"}
|
|
addon_name=${PKG_ADDON_NAME:-"$PKG_NAME"}
|
|
|
|
for f in $PKG_DIR/source/resources/screenshot-*.{jpg,png}; do
|
|
if [ -f "$f" ]; then
|
|
screenshots+="<screenshot>resources/${f##*/}</screenshot>\n"
|
|
fi
|
|
done
|
|
|
|
sed -e "s|@PKG_ADDON_ID@|$PKG_ADDON_ID|g" \
|
|
-e "s|@ADDON_NAME@|$addon_name|g" \
|
|
-e "s|@ADDON_VERSION@|$addon_version|g" \
|
|
-e "s|@REQUIRES@|$requires|g" \
|
|
-e "s|@PKG_SHORTDESC@|$PKG_SHORTDESC|g" \
|
|
-e "s|@OS_VERSION@|$OS_VERSION|g" \
|
|
-e "s|@PKG_LONGDESC@|$PKG_LONGDESC|g" \
|
|
-e "s|@PKG_DISCLAIMER@|$PKG_DISCLAIMER|g" \
|
|
-e "s|@PROVIDER_NAME@|$provider_name|g" \
|
|
-e "s|@PKG_ADDON_PROVIDES@|$PKG_ADDON_PROVIDES|g" \
|
|
-e "s|@PKG_ADDON_SCREENSHOT@|$screenshots|g" \
|
|
-e "s|@PKG_ADDON_BROKEN@|$PKG_ADDON_BROKEN|g" \
|
|
-i "$addon_xml"
|
|
}
|
|
|
|
install_addon_files() {
|
|
mkdir -p "$1"
|
|
|
|
install_addon_source "$1"
|
|
install_addon_images "$1"
|
|
create_addon_xml "$1"
|
|
python_fix_abi "$1"
|
|
|
|
if pkg_call_exists_opt post_install_addon; then
|
|
INSTALL="$1" pkg_call
|
|
fi
|
|
}
|
|
|
|
install_driver_addon_files() {
|
|
if [ "$#" -eq 0 ] ; then
|
|
die "$(print_color CLR_ERROR "no module search path defined")"
|
|
fi
|
|
|
|
PKG_MODULE_DIR="$INSTALL/$(get_full_module_dir $PKG_ADDON_ID)/updates/$PKG_ADDON_ID"
|
|
PKG_ADDON_DIR="$INSTALL/usr/share/$MEDIACENTER/addons/$PKG_ADDON_ID"
|
|
|
|
mkdir -p $PKG_MODULE_DIR
|
|
find $@ -name \*.ko -exec cp {} $PKG_MODULE_DIR \;
|
|
|
|
find $PKG_MODULE_DIR -name \*.ko -exec ${TARGET_KERNEL_PREFIX}strip --strip-debug {} \;
|
|
|
|
mkdir -p $PKG_ADDON_DIR
|
|
cp $PKG_DIR/changelog.txt $PKG_ADDON_DIR
|
|
install_addon_files "$PKG_ADDON_DIR"
|
|
}
|
|
|
|
|
|
### TARGET CONFIGURATION HELPERS ###
|
|
add_user() {
|
|
# Usage: add_user "username" "password" "userid" "groupid" "description" "home" "shell"
|
|
mkdir -p ${INSTALL}/etc
|
|
touch ${INSTALL}/etc/passwd
|
|
if ! grep -q "^$1:" ${INSTALL}/etc/passwd; then
|
|
echo "$1:x:$3:$4:$5:$6:$7" >> ${INSTALL}/etc/passwd
|
|
fi
|
|
|
|
mkdir -p ${INSTALL}/usr/cache
|
|
touch ${INSTALL}/usr/cache/shadow
|
|
ln -sf /storage/.cache/shadow ${INSTALL}/etc/shadow 2>/dev/null || true
|
|
|
|
PASSWORD="$2"
|
|
if [ "$PASSWORD" = "x" ]; then
|
|
PASSWORD="*"
|
|
else
|
|
PASSWORD=$(python -c "import crypt; print(crypt.crypt('$PASSWORD', crypt.mksalt(crypt.METHOD_SHA512)))")
|
|
fi
|
|
if ! grep -q "^$1:" ${INSTALL}/usr/cache/shadow; then
|
|
echo "$1:$PASSWORD:::::::" >> ${INSTALL}/usr/cache/shadow
|
|
fi
|
|
}
|
|
|
|
add_group() {
|
|
# Usage: add_group "groupname" "groupid" ("members")
|
|
mkdir -p ${INSTALL}/etc
|
|
touch ${INSTALL}/etc/group
|
|
if [ -z "`grep "$1:" ${INSTALL}/etc/group`" ]; then
|
|
echo "$1:x:$2:$3" >> ${INSTALL}/etc/group
|
|
fi
|
|
}
|
|
|
|
# Usage: enable_service <unit> [target]
|
|
enable_service() {
|
|
local unit="$1"
|
|
local unit_dir="usr/lib/systemd/system"
|
|
local target="$2"
|
|
local target_dir=$INSTALL
|
|
|
|
[ -f "$target_dir/$unit_dir/$unit" ] || die "ERROR: cannot enable non-existent service $target_dir/$unit_dir/$unit"
|
|
|
|
if [ -z "$target" ] ; then
|
|
for target in `grep '^WantedBy' $target_dir/$unit_dir/$unit | cut -f2 -d=` ; do
|
|
if [ -n "$target" ]; then
|
|
mkdir -p ${target_dir}/$unit_dir/${target}.wants
|
|
ln -sf ../${unit} ${target_dir}/$unit_dir/${target}.wants/
|
|
fi
|
|
done
|
|
fi
|
|
for target in `grep '^Alias' $target_dir/$unit_dir/$unit | cut -f2 -d=` ; do
|
|
if [ -n "$target" ]; then
|
|
ln -sf ${unit} ${target_dir}/$unit_dir/${target}
|
|
fi
|
|
done
|
|
}
|
|
|
|
|
|
### MULTI-THREADED FUNCTION HELPERS ###
|
|
# flocks: 94 (pkg_lock_status)
|
|
# 95 (scripts/pkgbuild)
|
|
# 96 (acquire_exclusive_lock)
|
|
# 97 (acquire_update_lock)
|
|
# 98 (pkg_lock)
|
|
# 99 (scripts/get)
|
|
|
|
# Test build type so that these functions are a no-op during non-multithreaded builds.
|
|
|
|
# Prevent concurrent modifications to a package during certain activities.
|
|
# With dynamic scheduling we now only need to acquire the lock
|
|
# during unpack and reconf, all other activities do not need to acquire a
|
|
# lock as there should be no concurrent access however the existing code path
|
|
# potentially generates useful logging for minimal cost so keep it.
|
|
#
|
|
# If a package is already locked and the owner is ourselves
|
|
# then assume we already have the required lock.
|
|
pkg_lock() {
|
|
is_sequential_build && return 0
|
|
|
|
local pkg="$1" task="$2" parent_pkg="$3"
|
|
local this_job="${MTJOBID}"
|
|
local lock_job lock_seq lock_task lock_pkg locked=no idwidth
|
|
|
|
if [ "${task}" = "unpack" -o "${task}" = "reconf" ]; then
|
|
exec 98>"${THREAD_CONTROL}/locks/${pkg}.${task}"
|
|
while [ : ]; do
|
|
read -r lock_job lock_seq lock_task lock_pkg <<<$(cat "${THREAD_CONTROL}/locks/${pkg}.${task}.owner" 2>/dev/null)
|
|
[ -n "${lock_job}" ] && break
|
|
flock --wait 1 --exclusive 98 && locked=yes && break
|
|
done
|
|
|
|
if [ "${locked}" = "no" -a "${lock_job}/${lock_seq}" != "${this_job}/${PARALLEL_SEQ}" ]; then
|
|
[ "${THREADCOUNT}" = "0" ] && idwidth=${#MTMAXJOBS} || idwidth=2
|
|
pkg_lock_status "STALLED" "${parent_pkg}" "${task}" "$(printf "waiting on [%0*d] %s %s" ${idwidth} ${lock_job} "${lock_task}" "${lock_pkg}")"
|
|
flock --exclusive 98
|
|
fi
|
|
fi
|
|
|
|
pkg_lock_status "LOCKED" "${pkg}" "${task}"
|
|
}
|
|
|
|
# Log additional information for a locked package.
|
|
pkg_lock_status() {
|
|
is_sequential_build && return 0
|
|
|
|
local status="$1" pkg="$2" task="$3" msg="$4"
|
|
local this_job="${MTJOBID}" line idwidth
|
|
|
|
[ "${THREADCOUNT}" = "0" ] && idwidth=${#MTMAXJOBS} || idwidth=2
|
|
|
|
(
|
|
flock --exclusive 94
|
|
|
|
# Write the configured number of slots to history to improve accuracy of later analysis
|
|
if [ ! -f "${THREAD_CONTROL}/history" ]; then
|
|
printf "%s: <%06d> [%0*d/%0*d] %-7s %-7s %s %s\n" \
|
|
"$(date +%Y-%m-%d\ %H:%M:%S.%N)" $$ ${idwidth} 0 ${#MTMAXJOBS} 0 "IDLE" "config" "info" "slots=${MTMAXSLOT};jobs=${MTMAXJOBS}" >>"${THREAD_CONTROL}/history"
|
|
fi
|
|
|
|
printf -v line "%s: <%06d> [%0*d/%0*d] %-7s %-7s %-35s" \
|
|
"$(date +%Y-%m-%d\ %H:%M:%S.%N)" $$ ${idwidth} ${this_job} ${#MTMAXJOBS} ${PARALLEL_SEQ:-0} "${status}" "${task}" "${pkg}"
|
|
[ -n "${msg}" ] && line+=" (${msg})"
|
|
|
|
echo "${line}" >>"${THREAD_CONTROL}/history"
|
|
|
|
if [ "${DASHBOARD}" != "no" ]; then
|
|
update_dashboard "${status}" "${pkg}" "${task}" "${msg}"
|
|
fi
|
|
) 94>"${THREAD_CONTROL}/locks/.history"
|
|
|
|
if [ "${status}" = "LOCKED" ]; then
|
|
echo "${this_job} ${PARALLEL_SEQ} ${task} ${pkg}" >"${THREAD_CONTROL}/locks/${pkg}.${task}.owner"
|
|
elif [ "${status}" = "UNLOCK" ]; then
|
|
rm "${THREAD_CONTROL}/locks/${pkg}.${task}.owner"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
update_dashboard() {
|
|
is_sequential_build && return 0
|
|
|
|
local status="$1" pkg="$2" task="$3" msg="$4"
|
|
local line preamble num elapsed projdevarch
|
|
local boldred boldgreen boldyellow endcolor idwidth
|
|
|
|
[ "${THREADCOUNT}" = "0" ] && idwidth=${#MTMAXSLOT} || idwidth=2
|
|
|
|
if [ ! -s ${THREAD_CONTROL}/status ]; then
|
|
echo "" >"${THREAD_CONTROL}/status"
|
|
echo "" >>"${THREAD_CONTROL}/status"
|
|
for i in $(seq 1 $((MTMAXSLOT))); do
|
|
printf "[%0*d/%0*d] %-7s\n" ${idwidth} ${i} ${#MTMAXJOBS} 0 "IDLE" >>"${THREAD_CONTROL}/status"
|
|
done
|
|
fi
|
|
|
|
num=$(< "${THREAD_CONTROL}/progress.prev")
|
|
projdevarch="${PROJECT}/"
|
|
[ -n "${DEVICE}" ] && projdevarch+="${DEVICE}/"
|
|
projdevarch+="${TARGET_ARCH}"
|
|
[ -n "${BUILD_SUFFIX}" ] && projdevarch+=", ${BUILD_SUFFIX}"
|
|
TZ=UTC0 printf -v elapsed "%(%H:%M:%S)T" $(($(date +%s) - MTBUILDSTART))
|
|
printf -v preamble "%s Dashboard (%s) - %d of %d jobs completed, %s elapsed" "${DISTRONAME}" "${projdevarch}" $((num + 1)) ${MTMAXJOBS} "${elapsed}"
|
|
printf -v preamble "%b%-105s %s" "\e[2J\e[0;0H" "${preamble}" "$(date "+%Y-%m-%d %H:%M:%S")"
|
|
|
|
if [ "${DISABLE_COLORS}" != "yes" ]; then
|
|
boldred="\e[1;31m"
|
|
boldgreen="\e[1;32m"
|
|
boldyellow="\e[1;33m"
|
|
white="\e[0;37m"
|
|
endcolor="\e[0m"
|
|
|
|
case "${status}" in
|
|
IDLE) color="${white}";;
|
|
STALLED) color="${boldyellow}";;
|
|
MUTEX/W) color="${boldyellow}";;
|
|
FAILED ) color="${boldred}";;
|
|
*) color="${boldgreen}";;
|
|
esac
|
|
fi
|
|
|
|
printf -v line "[%0*d/%0*d] %b%-7s%b %-7s %-35s" ${idwidth} ${MTJOBID} ${#MTMAXJOBS} ${PARALLEL_SEQ:-0} "${color}" "${status}" "${endcolor}" "${task}" "${pkg}"
|
|
[ -n "${msg}" ] && line+=" ${msg}"
|
|
|
|
sed -e "1s@.*@${preamble}@;$((MTJOBID + 2))s@.*@${line}@" -i "${THREAD_CONTROL}/status"
|
|
}
|
|
|
|
# Thread concurrency helpers to avoid concurrency issues with some code,
|
|
# eg. when Python installs directly into $TOOLCHAIN.
|
|
acquire_exclusive_lock() {
|
|
is_sequential_build && return 0
|
|
|
|
local pkg="$1" task="$2" lockfile="${3:-global}"
|
|
local this_job="${MTJOBID}"
|
|
local lock_job lock_seq lock_task lock_pkg locked=no idwidth
|
|
|
|
exec 96>"${THREAD_CONTROL}/locks/.mutex.${lockfile}"
|
|
while [ : ]; do
|
|
read -r lock_job lock_seq lock_task lock_pkg <<<$(cat "${THREAD_CONTROL}/locks/.mutex.${lockfile}.owner" 2>/dev/null)
|
|
[ -n "${lock_job}" ] && break
|
|
flock --wait 1 --exclusive 96 && locked=yes && break
|
|
done
|
|
|
|
if [ "${locked}" = "no" -a "${lock_job}/${lock_seq}" != "${this_job}/${PARALLEL_SEQ}" ]; then
|
|
[ "${THREADCOUNT}" = "0" ] && idwidth=${#MTMAXJOBS} || idwidth=2
|
|
pkg_lock_status "MUTEX/W" "${pkg}" "${task}" "$(printf "mutex: %s; waiting on [%0*d] %s %s" "${lockfile}" ${idwidth} ${lock_job} "${lock_task}" "${lock_pkg}")"
|
|
flock --exclusive 96
|
|
fi
|
|
|
|
pkg_lock_status "MUTEX" "${pkg}" "${task}" "mutex: ${lockfile}"
|
|
|
|
echo "${this_job} ${PARALLEL_SEQ} ${task} ${pkg}" >"${THREAD_CONTROL}/locks/.mutex.${lockfile}.owner"
|
|
}
|
|
|
|
release_exclusive_lock() {
|
|
is_sequential_build && return 0
|
|
|
|
local pkg="$1" task="$2" lockfile="${3:-global}"
|
|
|
|
pkg_lock_status "ACTIVE" "${pkg}" "${task}"
|
|
|
|
rm "${THREAD_CONTROL}/locks/.mutex.${lockfile}.owner"
|
|
flock --unlock 96 2>/dev/null
|
|
}
|
|
|
|
# Execute single command using mutex
|
|
exec_thread_safe() {
|
|
local result
|
|
acquire_exclusive_lock "${PKG_NAME:exec}" "execcmd"
|
|
$@
|
|
result=$?
|
|
release_exclusive_lock "${PKG_NAME:exec}" "execcmd"
|
|
return ${result}
|
|
}
|
|
|
|
# A lightweight target specific lock (eg. image, sysroot)
|
|
acquire_update_lock() {
|
|
is_sequential_build && return 0
|
|
|
|
exec 97>"${THREAD_CONTROL}/locks/.update.${1}"
|
|
flock --exclusive 97
|
|
}
|
|
|
|
release_update_lock() {
|
|
is_sequential_build && return 0
|
|
|
|
flock --unlock 97 2>/dev/null
|
|
}
|
|
|
|
# Use distribution functions if any
|
|
if [ -f "distributions/$DISTRO/config/functions" ]; then
|
|
. distributions/$DISTRO/config/functions
|
|
fi
|