Mailing List Archive

[PATCH v3] llvm-r1.eclass: Initial version
See-Also: https://bugs.gentoo.org/923228
See-Also: https://bugs.gentoo.org/880671
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
--
2.43.0
Re: [PATCH v3] llvm-r1.eclass: Initial version [ In reply to ]
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