From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by finch.gentoo.org (Postfix) with ESMTPS id A37FC15815E for ; Fri, 9 Feb 2024 17:00:46 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id 53D84E2A78; Fri, 9 Feb 2024 17:00:42 +0000 (UTC) Received: from smtp.gentoo.org (mail.gentoo.org [IPv6:2001:470:ea4a:1:5054:ff:fec7:86e4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id F1503E2A4C for ; Fri, 9 Feb 2024 17:00:41 +0000 (UTC) From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= To: gentoo-dev@lists.gentoo.org Cc: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Subject: [gentoo-dev] [PATCH v3] llvm-r1.eclass: Initial version Date: Fri, 9 Feb 2024 17:59:53 +0100 Message-ID: <20240209170024.161215-2-mgorny@gentoo.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240207203515.17640-6-mgorny@gentoo.org> References: <20240207203515.17640-6-mgorny@gentoo.org> Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-dev@lists.gentoo.org Reply-to: gentoo-dev@lists.gentoo.org X-Auto-Response-Suppress: DR, RN, NRN, OOF, AutoReply MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Archives-Salt: 51563aa1-48ba-4dcf-8170-1ad2248737f4 X-Archives-Hash: 763719d1b0117ac5c27680afb9a11572 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 --- 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 +# @AUTHOR: +# Michał Górny +# @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: +# @DESCRIPTION: +# Output a dependency block, repeating "" 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} " + + 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