• Re: [gentoo-dev] [PATCH v3] llvm-r1.eclass: Initial version

    From Sam James@21:1/5 to mgorny@gentoo.org on Sat Feb 10 01:00:02 2024
    Michał Górny <mgorny@gentoo.org> writes:

    See-Also: https://bugs.gentoo.org/923228
    See-Also: https://bugs.gentoo.org/880671

    s/See-Also/Bug/.

    LGTM otherwise. Thanks for working on this, it's a great improvement.

    Closes: https://bugs.gentoo.org/821955
    Closes: https://bugs.gentoo.org/919150
    Signed-off-by: Michał Górny <mgorny@gentoo.org>
    ---
    eclass/llvm-r1.eclass | 250 ++++++++++++++++++++++++++++++++++++++++
    eclass/tests/llvm-r1.sh | 151 ++++++++++++++++++++++++
    2 files changed, 401 insertions(+)
    create mode 100644 eclass/llvm-r1.eclass
    create mode 100755 eclass/tests/llvm-r1.sh

    Changed in v3: added LLVM_OPTIONAL to avoid unconditionally setting REQUIRED_USE when the ebuild doesn't require LLVM unconditionally.
    It also controls pkg_setup export.

    diff --git a/eclass/llvm-r1.eclass b/eclass/llvm-r1.eclass
    new file mode 100644
    index 000000000000..658946a1ecbd
    --- /dev/null
    +++ b/eclass/llvm-r1.eclass
    @@ -0,0 +1,250 @@
    +# Copyright 2024 Gentoo Authors
    +# Distributed under the terms of the GNU General Public License v2
    +
    +# @ECLASS: llvm-r1.eclass
    +# @MAINTAINER:
    +# Michał Górny <mgorny@gentoo.org>
    +# @AUTHOR:
    +# Michał Górny <mgorny@gentoo.org>
    +# @SUPPORTED_EAPIS: 8
    +# @PROVIDES: llvm-utils
    +# @BLURB: Provide LLVM_SLOT to build against slotted LLVM
    +# @DESCRIPTION:
    +# An eclass to reliably depend on a set of LLVM-related packages
    +# in a matching slot. To use the eclass:
    +#
    +# 1. Set LLVM_COMPAT to the list of supported LLVM slots.
    +# 2. Use llvm_gen_dep and/or LLVM_USEDEP to add appropriate
    +# dependencies.
    +# 3. Use llvm-r1_pkg_setup, get_llvm_prefix or LLVM_SLOT.
    +#
    +# The eclass sets IUSE and REQUIRED_USE. The flag corresponding
    +# to the newest supported stable LLVM slot (or the newest testing,
    +# if no stable slots are supported) is enabled by default.
    +#
    +# Example:
    +# @CODE
    +# LLVM_COMPAT=( {16..18} )
    +#
    +# inherit llvm-r1
    +#
    +# DEPEND="
    +# dev-libs/libfoo[${LLVM_USEDEP}]
    +# $(llvm_gen_dep '
    +# sys-devel/clang:${LLVM_SLOT}
    +# sys-devel/llvm:${LLVM_SLOT}
    +# ')
    +# "
    +# @CODE
    +
    +case ${EAPI} in
    + 8) ;;
    + *) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
    +esac
    +
    +if [[ ! ${_LLVM_R1_ECLASS} ]]; then
    +_LLVM_R1_ECLASS=1
    +
    +inherit llvm-utils
    +
    +# == internal control knobs ==
    +
    +# @ECLASS_VARIABLE: _LLVM_OLDEST_SLOT
    +# @INTERNAL
    +# @DESCRIPTION:
    +# Oldest supported LLVM slot. This is used to automatically filter out
    +# unsupported LLVM_COMPAT values.
    +_LLVM_OLDEST_SLOT=15
    +
    +# @ECLASS_VARIABLE: _LLVM_NEWEST_STABLE
    +# @INTERNAL
    +# @DESCRIPTION:
    +# The newest stable LLVM version. Versions newer than that won't
    +# be automatically enabled via USE defaults.
    +_LLVM_NEWEST_STABLE=17
    +
    +# == control variables ==
    +
    +# @ECLASS_VARIABLE: LLVM_COMPAT
    +# @PRE_INHERIT
    +# @REQUIRED
    +# @DESCRIPTION:
    +# A list of LLVM slots supported by the package, oldest to newest.
    +#
    +# Example:
    +# @CODE
    +# LLVM_COMPAT=( {15..17} )
    +# @CODE
    +
    +# @ECLASS_VARIABLE: LLVM_OPTIONAL
    +# @PRE_INHERIT
    +# @DEFAULT_UNSET
    +# @DESCRIPTION:
    +# If set to a non-empty value, disables setting REQUIRED_USE
    +# and exporting pkg_setup. You have to add LLVM_REQUIRED_USE and call
    +# pkg_setup manually, with appropriate USE conditions.
    +
    +# == global metadata ==
    +
    +# @ECLASS_VARIABLE: LLVM_REQUIRED_USE
    +# @OUTPUT_VARIABLE
    +# @DESCRIPTION:
    +# An eclass-generated REQUIRED_USE string that enforces selecting
    +# exactly one slot. It LLVM_OPTIONAL is set, it needs to be copied
    +# into REQUIRED_USE, under appropriate USE conditions. Otherwise,
    +# it is added automatically.
    +
    +# @ECLASS_VARIABLE: LLVM_USEDEP
    +# @OUTPUT_VARIABLE
    +# @DESCRIPTION:
    +# An eclass-generated USE dependency string that can be applied to other
    +# packages using the same eclass, to enforce a LLVM slot match.
    +
    +_llvm_set_globals() {
    + debug-print-function ${FUNCNAME} "${@}"
    +
    + if [[ ${LLVM_COMPAT@a} != *a* ]]; then
    + die "LLVM_COMPAT must be set to an array before inheriting ${ECLASS}"
    + fi
    +
    + local stable=() unstable=()
    + local x
    + for x in "${LLVM_COMPAT[@]}"; do
    + if [[ ${x} -gt ${_LLVM_NEWEST_STABLE} ]]; then
    + unstable+=( "${x}" )
    + elif [[ ${x} -ge ${_LLVM_OLDEST_SLOT} ]]; then
    + stable+=( "${x}" )
    + fi
    + done
    +
    + _LLVM_SLOTS=( "${stable[@]}" "${unstable[@]}" )
    + if [[ ! ${_LLVM_SLOTS[@]} ]]; then
    + die "LLVM_COMPAT does not contain any valid versions (all older than ${_LLVM_OLDEST_SLOT}?)"
    + fi
    +
    + if [[ ${stable[@]} ]]; then
    + IUSE="+llvm_slot_${stable[-1]}"
    + unset 'stable[-1]'
    + else
    + IUSE="+llvm_slot_${unstable[-1]}"
    + unset 'unstable[-1]'
    + fi
    + local nondefault=( "${stable[@]}" "${unstable[@]}" )
    + IUSE+=" ${nondefault[*]/#/llvm_slot_}"
    +
    + local flags=( "${_LLVM_SLOTS[@]/#/llvm_slot_}" )
    + LLVM_REQUIRED_USE="^^ ( ${flags[*]} )"
    + local usedep_flags=${flags[*]/%/(-)?}
    + LLVM_USEDEP=${usedep_flags// /,}
    + readonly LLVM_REQUIRED_USE LLVM_USEDEP
    +
    + if [[ ! ${LLVM_OPTIONAL} ]]; then
    + REQUIRED_USE=${LLVM_REQUIRED_USE}
    + fi
    +}
    +_llvm_set_globals
    +unset -f _llvm_set_globals
    +
    +# == metadata helpers ==
    +
    +# @FUNCTION: llvm_gen_dep
    +# @USAGE: <dependency>
    +# @DESCRIPTION:
    +# Output a dependency block, repeating "<dependency>" conditionally
    +# to all llvm_slot_* USE flags. Any occurences of '${LLVM_SLOT}'
    +# within the block will be substituted for the respective slot.
    +#
    +# Example:
    +# @CODE
    +# DEPEND="
    +# $(llvm_gen_dep '
    +# sys-devel/clang:${LLVM_SLOT}
    +# sys-devel/llvm:${LLVM_SLOT}
    +# ')
    +# "
    +# @CODE
    +llvm_gen_dep() {
    + debug-print-function ${FUNCNAME} "${@}"
    +
    + [[ ${#} -ne 1 ]] && die "Usage: ${FUNCNAME} <dependency>"
    +
    + local dep=${1}
    +
    + local slot
    + for slot in "${_LLVM_SLOTS[@]}"; do
    + echo "llvm_slot_${slot}? ( ${dep//\$\{LLVM_SLOT\}/${slot}} )"
    + done
    +}
    +
    +# == ebuild helpers ==
    +
    +# @FUNCTION: get_llvm_prefix
    +# @USAGE: [-b|-d]
    +# @DESCRIPTION:
    +# Output the path to the selected LLVM slot.
    +#
    +# With no option or "-d", the path is prefixed by ESYSROOT. LLVM
    +# dependencies should be in DEPEND then.
    +#
    +# With "-b" option, the path is prefixed by BROOT. LLVM dependencies
    +# should be in BDEPEND then.
    +get_llvm_prefix() {
    + debug-print-function ${FUNCNAME} "${@}"
    +
    + [[ ${#} -gt 1 ]] && die "Usage: ${FUNCNAME} [-b|-d]"
    +
    + local prefix
    + case ${1--d} in
    + -d)
    + prefix=${ESYSROOT}
    + ;;
    + -b)
    + prefix=${BROOT}
    + ;;
    + *)
    + die "${FUNCNAME}: invalid option: ${1}"
    + ;;
    + esac
    +
    + echo "${prefix}/usr/lib/llvm/${LLVM_SLOT}"
    +}
    +
    +# @FUNCTION: llvm-r1_pkg_setup
    +# @DESCRIPTION:
    +# Prepend the appropriate executable directory for the selected LLVM
    +# slot to PATH.
    +#
    +# The PATH manipulation is only done for source builds. The function
    +# is a no-op when installing a binary package.
    +#
    +# If any other behavior is desired, the contents of the function
    +# should be inlined into the ebuild and modified as necessary.
    +#
    +# Note that this function is not exported if LLVM_OPTIONAL is set.
    +# In that case, it needs to be called manually.
    +llvm-r1_pkg_setup() {
    + debug-print-function ${FUNCNAME} "${@}"
    +
    + if [[ ${MERGE_TYPE} != binary ]]; then
    + [[ -z ${LLVM_SLOT} ]] && die "LLVM_SLOT unset (broken USE_EXPAND?)"
    +
    + llvm_fix_clang_version CC CPP CXX
    + # keep in sync with profiles/features/llvm/make.defaults!
    + llvm_fix_tool_path ADDR2LINE AR AS LD NM OBJCOPY OBJDUMP RANLIB + llvm_fix_tool_path READELF STRINGS STRIP
    +
    + # Set LLVM_CONFIG to help Meson (bug #907965) but only do it
    + # for empty ESYSROOT (as a proxy for "are we cross-compiling?").
    + if [[ -z ${ESYSROOT} ]] ; then
    + llvm_fix_tool_path LLVM_CONFIG
    + fi
    +
    + llvm_prepend_path "${LLVM_SLOT}"
    + fi
    +}
    +
    +fi
    +
    +if [[ ! ${LLVM_OPTIONAL} ]]; then
    + EXPORT_FUNCTIONS pkg_setup
    +fi
    diff --git a/eclass/tests/llvm-r1.sh b/eclass/tests/llvm-r1.sh
    new file mode 100755
    index 000000000000..9958f5bba420
    --- /dev/null
    +++ b/eclass/tests/llvm-r1.sh
    @@ -0,0 +1,151 @@
    +#!/bin/bash
    +# Copyright 2024 Gentoo Authors
    +# Distributed under the terms of the GNU General Public License v2
    +
    +source tests-common.sh || exit
    +
    +EAPI=8
    +
    +test_globals() {
    + local compat=${1}
    + local expected_iuse=${2}
    + local expected_required_use=${3}
    + local expected_usedep=${4}
    + local x
    +
    + tbegin "LLVM_COMPAT=( ${compat} )"
    +
    + (
    + local fail=0
    + local LLVM_COMPAT=( ${compat} )
    +
    + inherit llvm-r1
    +
    + if [[ ${IUSE%% } != ${expected_iuse} ]]; then
    + eerror " IUSE: ${IUSE%% }"
    + eerror "does not match: ${expected_iuse}"
    + fail=1
    + fi
    +
    + if [[ ${REQUIRED_USE} != ${expected_required_use} ]]; then
    + eerror " REQUIRED_USE: ${REQUIRED_USE}"
    + eerror "does not match: ${expected_required_use}"
    + fail=1
    + fi
    +
    + if [[ ${LLVM_USEDEP} != ${expected_usedep} ]]; then
    + eerror " LLVM_USEDEP: ${LLVM_USEDEP}"
    + eerror "does not match: ${expected_usedep}"
    + fail=1
    + fi
    +
    + exit "${fail}"
    + )
    +
    + tend "${?}"
    +}
    +
    +test_gen_dep() {
    + local arg=${1}
    + local expected
    + read -r -d '' expected
    +
    + tbegin "llvm_gen_dep ${arg}"
    + local value=$(llvm_gen_dep "${arg}")
    +
    + if [[ ${value} != ${expected} ]]; then
    + eerror "python_get_usedep ${arg}"
    + eerror "gave:"
    + eerror " ${value}"
    + eerror "expected:"
    + eerror " ${expected}"
    + fi
    + tend ${?}
    +}
    +
    +test_fix_clang_version() {
    + local var=${1}
    + local tool=${2}
    + local version=${3}
    + local expected=${4}
    +
    + eval "${tool}() {
    + cat <<-EOF
    + clang version ${version}
    + Target: x86_64-pc-linux-gnu
    + Thread model: posix
    + InstalledDir: /usr/lib/llvm/17/bin
    + Configuration file: /etc/clang/x86_64-pc-linux-gnu-clang.cfg
    + EOF
    + }"
    +
    + declare -g ${var}=${tool}
    + tbegin "llvm_fix_clang_version ${var}=${tool} for ${version}"
    + llvm_fix_clang_version "${var}"
    + if [[ ${!var} != ${expected} ]]; then
    + eerror "llvm_fix_clang_version ${var}"
    + eerror " gave: ${!var}"
    + eerror "expected: ${expected}"
    + fi
    + tend ${?}
    +}
    +
    +test_fix_tool_path() {
    + local var=${1}
    + local tool=${2}
    + local expected_subst=${3}
    + local expected=${tool}
    +
    + tbegin "llvm_fix_tool_path ${1}=${2} (from llvm? ${expected_subst})"
    +
    + local matches=( "${BROOT}"/usr/lib/llvm/*/bin/"${tool}" )
    + if [[ ${expected_subst} == 1 ]]; then
    + if [[ ! -x ${matches[0]} ]]; then
    + ewarn "- skipping, test requires ${tool}"
    + return
    + fi
    +
    + expected=${matches[0]}
    + local -x PATH=${matches[0]%/*}
    + else
    + local -x PATH=
    + fi
    +
    + declare -g ${var}=${tool}
    + llvm_fix_tool_path "${var}"
    + if [[ ${!var} != ${expected} ]]; then
    + eerror "llvm_fix_tool_path ${var}"
    + eerror " gave: ${!var}"
    + eerror "expected: ${expected}"
    + fi
    + tend ${?}
    +}
    +
    +test_globals '14 15 16 17 18' \
    + "+llvm_slot_17 llvm_slot_15 llvm_slot_16 llvm_slot_18" \
    + "^^ ( llvm_slot_15 llvm_slot_16 llvm_slot_17 llvm_slot_18 )" \
    + "llvm_slot_15(-)?,llvm_slot_16(-)?,llvm_slot_17(-)?,llvm_slot_18(-)?" +test_globals '14 15 16' \
    + "+llvm_slot_16 llvm_slot_15" \
    + "^^ ( llvm_slot_15 llvm_slot_16 )" \
    + "llvm_slot_15(-)?,llvm_slot_16(-)?"
    +test_globals '15 18' \
    + "+llvm_slot_15 llvm_slot_18" \
    + "^^ ( llvm_slot_15 llvm_slot_18 )" \
    + "llvm_slot_15(-)?,llvm_slot_18(-)?"
    +test_globals '18' \
    + "+llvm_slot_18" \
    + "^^ ( llvm_slot_18 )" \
    + "llvm_slot_18(-)?"
    +
    +LLVM_COMPAT=( {14..18} )
    +inherit llvm-r1
    +
    +test_gen_dep 'sys-devel/llvm:${LLVM_SLOT} sys-devel/clang:${LLVM_SLOT}' <<-EOF
    + llvm_slot_15? ( sys-devel/llvm:15 sys-devel/clang:15 )
    + llvm_slot_16? ( sys-devel/llvm:16 sys-devel/clang:16 )
    + llvm_slot_17? ( sys-devel/llvm:17 sys-devel/clang:17 )
    + llvm_slot_18? ( sys-devel/llvm:18 sys-devel/clang:18 )
    +EOF
    +
    +texit


    --=-=-Content-Type: application/pgp-signature; name="signature.asc"

    -----BEGIN PGP SIGNATURE-----

    iOUEARYKAI0WIQQlpruI3Zt2TGtVQcJzhAn1IN+RkAUCZca6GV8UgAAAAAAuAChp c3N1ZXItZnByQG5vdGF0aW9ucy5vcGVucGdwLmZpZnRoaG9yc2VtYW4ubmV0MjVB NkJCODhERDlCNzY0QzZCNTU0MUMyNzM4NDA5RjUyMERGOTE5MA8cc2FtQGdlbnRv by5vcmcACgkQc4QJ9SDfkZDRnQEAuXpF34ofdopbh/h193GOEb90PAJuVgjywe3g YZUV7gsA/jomQqckf+Pyy9SumW+xrFgXOxRvG4qGvUTO7LlDHggK
    =k9jh
    -----END PGP SIGNATURE-----

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)