diff --git a/config/functions b/config/functions index b6cf71aeaa..58364d45ce 100644 --- a/config/functions +++ b/config/functions @@ -1,3 +1,112 @@ +### FUNCTION HELPERS ### +# 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. removefromlist "abc def ghi" "(abc|def)" will work). +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 + + if [ "$DISABLE_COLORS" = "yes" ]; then + [ $# -eq 2 ] && echo -en "${clr_text}" + return 0 + fi + + clr_actual="${!clr_name}" + + # If $clr_name isn't another colour variable (boldgreen etc.), then + # assume it's an actual colour escape sequence. + # + # Otherwise if $clr_name isn't defined, or doesn't exist, then use + # standard colours. + # + if [[ -n "${clr_actual}" ]] && [[ -z "${!clr_actual}" ]]; then + clr_actual="${clr_name}" + elif [[ -z "${clr_actual}" ]] || [[ -z "${!clr_actual}" ]]; then + 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_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 +} + + +### BUILDSYSTEM HELPERS ### +# check if a flag is enabled +# $1: flag-name, $2: default (yes/no), $3: ingenious check (none,only-disable,only-enable) +# set variable PKG_[FLAG]_[HOST/TARGET]_ENABLED=(yes/no) +# return 0 if flag is enabled, otherwise 1 +flag_enabled() { + # make flag name upper case and replace hyphen with underscore, to use as variable name + local flag_name=${1^^} + [[ $flag_name =~ : ]] || flag_name+="_TARGET" + flag_name="PKG_${flag_name//[:-]/_}_ENABLED" + + # check flag + if [ -n "${PKG_BUILD_FLAGS}" ] && listcontains "${PKG_BUILD_FLAGS}" "[+]?$1"; then + if [ "${3:none}" = "only-disable" ]; then + echo "ERROR: $1 can not enable via PKG_BUILD_FLAGS (found in $PKG_NAME)" + exit 1 + fi + declare ${flag_name}="yes" + return 0 + elif [ "$2" = "yes" ] && ! listcontains "${PKG_BUILD_FLAGS}" "-$1"; then + declare ${flag_name}="yes" + return 0 + else + if [ "${3:none}" = "only-enable" ]; then + echo "ERROR: $1 can not disable via PKG_BUILD_FLAGS (found in $PKG_NAME)" + exit 1 + fi + declare ${flag_name}="no" + return 1 + fi +} + setup_toolchain() { if [ "$LTO_SUPPORT" = "yes" ]; then if flag_enabled "lto-parallel" "no"; then @@ -220,260 +329,6 @@ reset_pkg_vars() { PKG_PYTHON_VERSION="python2.7" } -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_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" && break - done -} - -kernel_make() { - 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_build_dir linux)/.install_pkg/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" -} - -# get package's build dir -get_build_dir() { - local _PKG_NAME="$(get_pkg_variable "$1" PKG_NAME)" _PKG_VERSION="$(get_pkg_version "$1")" - if [ -n "$_PKG_NAME" -a -n "$_PKG_VERSION" ]; then - echo $BUILD/${_PKG_NAME}-${_PKG_VERSION} - fi -} - -get_pkg_version() { - get_pkg_variable "$1" PKG_VERSION -} - -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 - exit 1 - fi - - echo "$_PKG_DIR" -} - -# 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 - cd $ROOT - . config/options $1 &>/dev/null - fi - echo "${!2}" - fi -} - -calculate_stamp() { - local stamp - stamp="$PKG_DIR $PROJECT_DIR/$PROJECT/patches/$PKG_NAME" - [ -n "$DEVICE" ] && stamp+=" $PROJECT_DIR/$PROJECT/devices/$DEVICE/patches/$PKG_NAME" - [ -n "$PKG_NEED_UNPACK" ] && stamp+=" $PKG_NEED_UNPACK" - find ${stamp} -exec sha256sum {} \; 2>/dev/null | sed "s/ ${ROOT//\//\\/}\// /" | sort | sha256sum | cut -d" " -f1 -} - -# 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. removefromlist "abc def ghi" "(abc|def)" will work). -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[@]}" -} - -# check if a flag is enabled -# $1: flag-name, $2: default (yes/no), $3: ingenious check (none,only-disable,only-enable) -# set variable PKG_[FLAG]_[HOST/TARGET]_ENABLED=(yes/no) -# return 0 if flag is enabled, otherwise 1 -flag_enabled() { - # make flag name upper case and replace hyphen with underscore, to use as variable name - local flag_name=${1^^} - [[ $flag_name =~ : ]] || flag_name+="_TARGET" - flag_name="PKG_${flag_name//[:-]/_}_ENABLED" - - # check flag - if [ -n "${PKG_BUILD_FLAGS}" ] && listcontains "${PKG_BUILD_FLAGS}" "[+]?$1"; then - if [ "${3:none}" = "only-disable" ]; then - echo "ERROR: $1 can not enable via PKG_BUILD_FLAGS (found in $PKG_NAME)" - exit 1 - fi - declare ${flag_name}="yes" - return 0 - elif [ "$2" = "yes" ] && ! listcontains "${PKG_BUILD_FLAGS}" "-$1"; then - declare ${flag_name}="yes" - return 0 - else - if [ "${3:none}" = "only-enable" ]; then - echo "ERROR: $1 can not disable via PKG_BUILD_FLAGS (found in $PKG_NAME)" - exit 1 - fi - declare ${flag_name}="no" - return 1 - fi -} - -target_has_feature() { - listcontains "$TARGET_FEATURES" "$1" -} - -# 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" -} - set_debug_depends() { local pkg dep_pkg map tmp_array mpkg bpkg kvpair @@ -549,242 +404,16 @@ build_with_debug() { return 1 } -install_binary_addon() { - local addon_id="$1" addon_so - - mkdir -p $ADDON_BUILD/$addon_id/ - cp -R $PKG_BUILD/.install_pkg/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_BUILD/.install_pkg/usr/lib/$MEDIACENTER/addons/$addon_id/$addon_so $ADDON_BUILD/$addon_id/ - chmod +x $ADDON_BUILD/$addon_id/$addon_so - fi - - if [ -d $PKG_BUILD/.install_pkg/usr/lib/kernel-overlays/$addon_id ] ; then - mkdir -p $ADDON_BUILD/$addon_id/kernel-overlay - cp -PR $PKG_BUILD/.install_pkg/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 - - 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 " -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 [ ! $(which xmlstarlet) ]; then - echo "*** ERROR: $ADDON has addon.xml shipped, you need 'xmlstarlet' ***" - exit 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 - sed -e "/@PKG_ADDON_NEWS@/ \ - { - r $PKG_DIR/changelog.txt - d - }" -i "$addon_xml" - else - sed -e "s|@PKG_ADDON_NEWS@||g" -i "$addon_xml" - fi - - provider_name=${PKG_MAINTAINER:-"Team LibreELEC"} - addon_name=${PKG_ADDON_NAME:-"$PKG_NAME"} - - for f in $PKG_DIR/source/resources/screenshot-*.{jpg,png}; do - if [ -f "$f" ]; then - screenshots+="resources/$(basename $f)\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_driver_addon_files() { - if [ "$#" -eq 0 ] ; then - printf "$(print_color CLR_ERROR "no module search path defined")\n" +# strip +debug_strip() { + if [ -z "${BUILD_WITH_DEBUG}" ]; then + echo "ERROR: debug_strip() must not be called without configuring BUILD_WITH_DEBUG" >&2 exit 1 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" -} - -install_addon_files() { - install_addon_source "$1" - install_addon_images "$1" - create_addon_xml "$1" -} - -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 + if [ "${BUILD_WITH_DEBUG}" != "yes" ] && flag_enabled "strip" "yes"; then + find $* -type f -executable | xargs $STRIP 2>/dev/null || : fi - - mkdir -p ${INSTALL}/usr/cache - touch ${INSTALL}/usr/cache/shadow - ln -sf /storage/.cache/shadow ${INSTALL}/etc/shadow - - PASSWORD="$2" - if [ "$PASSWORD" = "x" ]; then - PASSWORD="*" - 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 -} - -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 - - 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 -} - -fix_module_depends() { - # modify .modinfo section in kernel module to depends on other required modules - local MODULE="$1" - local DEPENDS="$2" - local OLD_DEPENDS="" - cp ${MODULE} ${MODULE}_orig - $OBJDUMP -s -j .modinfo ${MODULE}_orig | awk 'BEGIN{v=0;} /Contents/ {v=1; next;} {if (v==1) print $0;}' >new.modinfo1 - cat new.modinfo1 | cut -c7-41 | awk '{printf($0);}' | sed 's/ //g;s/../\\\x&/g;' >new.modinfo2 - /bin/echo -ne `cat new.modinfo2` | tr '\000' '\n' >new.modinfo3 - cat new.modinfo3 | awk '/^depends=/ {next;} {print $0;}' | tr '\n' '\000' >new.modinfo - OLD_DEPENDS=$(awk '{FS="="} /depends=/ {print $2}' new.modinfo3) - [ -n "$OLD_DEPENDS" ] && DEPENDS="$OLD_DEPENDS,$DEPENDS" - /bin/echo -ne "depends=$DEPENDS\0" >>new.modinfo - $OBJCOPY --remove-section=.modinfo --add-section=.modinfo=new.modinfo --set-section-flags .modinfo=contents,alloc,load,readonly,data ${MODULE}_orig ${MODULE} - rm new.modinfo* -} - -# Usage: enable_service [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" ] || exit 1 - 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 } init_package_cache() { @@ -901,68 +530,450 @@ check_config() { fi } -# strip -debug_strip() { - if [ -z "${BUILD_WITH_DEBUG}" ]; then - echo "ERROR: debug_strip() must not be called without configuring BUILD_WITH_DEBUG" >&2 - exit 1 +do_autoreconf() { + export ACLOCAL_DIR=$SYSROOT_PREFIX/usr/share/aclocal + + if [ -e "$TOOLCHAIN/bin/autoconf" ]; then + export AUTOCONF=$TOOLCHAIN/bin/autoconf fi - if [ "${BUILD_WITH_DEBUG}" != "yes" ] && flag_enabled "strip" "yes"; then - find $* -type f -executable | xargs $STRIP 2>/dev/null || : + 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 + + 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 } -print_color() { - local clr_name="$1" clr_text="$2" clr_actual - if [ "$DISABLE_COLORS" = "yes" ]; then - [ $# -eq 2 ] && echo -en "${clr_text}" +### 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 + cd $ROOT + . config/options $1 &>/dev/null + fi + echo "${!2}" + fi +} + +# get package's build dir +get_build_dir() { + local _PKG_NAME="$(get_pkg_variable "$1" PKG_NAME)" _PKG_VERSION="$(get_pkg_version "$1")" + if [ -n "$_PKG_NAME" -a -n "$_PKG_VERSION" ]; then + echo $BUILD/${_PKG_NAME}-${_PKG_VERSION} + fi +} + +get_pkg_version() { + get_pkg_variable "$1" PKG_VERSION +} + +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 + exit 1 + fi + + echo "$_PKG_DIR" +} + +calculate_stamp() { + local stamp + stamp="$PKG_DIR $PROJECT_DIR/$PROJECT/patches/$PKG_NAME" + [ -n "$DEVICE" ] && stamp+=" $PROJECT_DIR/$PROJECT/devices/$DEVICE/patches/$PKG_NAME" + [ -n "$PKG_NEED_UNPACK" ] && stamp+=" $PKG_NEED_UNPACK" + find ${stamp} -exec sha256sum {} \; 2>/dev/null | sed "s/ ${ROOT//\//\\/}\// /" | sort | sha256sum | cut -d" " -f1 +} + +target_has_feature() { + listcontains "$TARGET_FEATURES" "$1" +} + +# 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 - fi - - clr_actual="${!clr_name}" - - # If $clr_name isn't another colour variable (boldgreen etc.), then - # assume it's an actual colour escape sequence. - # - # Otherwise if $clr_name isn't defined, or doesn't exist, then use - # standard colours. - # - if [[ -n "${clr_actual}" ]] && [[ -z "${!clr_actual}" ]]; then - clr_actual="${clr_name}" - elif [[ -z "${clr_actual}" ]] || [[ -z "${!clr_actual}" ]]; then - 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_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}" + 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" +} + + +### 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_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" && break + done +} + +kernel_make() { + 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_build_dir linux)/.install_pkg/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" +} + +fix_module_depends() { + # modify .modinfo section in kernel module to depends on other required modules + local MODULE="$1" + local DEPENDS="$2" + local OLD_DEPENDS="" + cp ${MODULE} ${MODULE}_orig + $OBJDUMP -s -j .modinfo ${MODULE}_orig | awk 'BEGIN{v=0;} /Contents/ {v=1; next;} {if (v==1) print $0;}' >new.modinfo1 + cat new.modinfo1 | cut -c7-41 | awk '{printf($0);}' | sed 's/ //g;s/../\\\x&/g;' >new.modinfo2 + /bin/echo -ne `cat new.modinfo2` | tr '\000' '\n' >new.modinfo3 + cat new.modinfo3 | awk '/^depends=/ {next;} {print $0;}' | tr '\n' '\000' >new.modinfo + OLD_DEPENDS=$(awk '{FS="="} /depends=/ {print $2}' new.modinfo3) + [ -n "$OLD_DEPENDS" ] && DEPENDS="$OLD_DEPENDS,$DEPENDS" + /bin/echo -ne "depends=$DEPENDS\0" >>new.modinfo + $OBJCOPY --remove-section=.modinfo --add-section=.modinfo=new.modinfo --set-section-flags .modinfo=contents,alloc,load,readonly,data ${MODULE}_orig ${MODULE} + rm new.modinfo* +} + + +### ADDON HELPERS ### +install_binary_addon() { + local addon_id="$1" addon_so + + mkdir -p $ADDON_BUILD/$addon_id/ + cp -R $PKG_BUILD/.install_pkg/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_BUILD/.install_pkg/usr/lib/$MEDIACENTER/addons/$addon_id/$addon_so $ADDON_BUILD/$addon_id/ + chmod +x $ADDON_BUILD/$addon_id/$addon_so + fi + + if [ -d $PKG_BUILD/.install_pkg/usr/lib/kernel-overlays/$addon_id ] ; then + mkdir -p $ADDON_BUILD/$addon_id/kernel-overlay + cp -PR $PKG_BUILD/.install_pkg/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 + + 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 " +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 [ ! $(which xmlstarlet) ]; then + echo "*** ERROR: $ADDON has addon.xml shipped, you need 'xmlstarlet' ***" + exit 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 + sed -e "/@PKG_ADDON_NEWS@/ \ + { + r $PKG_DIR/changelog.txt + d + }" -i "$addon_xml" + else + sed -e "s|@PKG_ADDON_NEWS@||g" -i "$addon_xml" + fi + + provider_name=${PKG_MAINTAINER:-"Team LibreELEC"} + addon_name=${PKG_ADDON_NAME:-"$PKG_NAME"} + + for f in $PKG_DIR/source/resources/screenshot-*.{jpg,png}; do + if [ -f "$f" ]; then + screenshots+="resources/$(basename $f)\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() { + install_addon_source "$1" + install_addon_images "$1" + create_addon_xml "$1" +} + +install_driver_addon_files() { + if [ "$#" -eq 0 ] ; then + printf "$(print_color CLR_ERROR "no module search path defined")\n" + exit 1 + 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 + + PASSWORD="$2" + if [ "$PASSWORD" = "x" ]; then + PASSWORD="*" + 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 [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" ] || exit 1 + 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 +} + # Use distribution functions if any if [ -f "distributions/$DISTRO/config/functions" ]; then . distributions/$DISTRO/config/functions