summaryrefslogtreecommitdiff
path: root/eclass/llvm.eclass
blob: 16596ec2ea661ea4fbc6bde5ea277729bef4cd9b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# Copyright 1999-2022 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

# @ECLASS: llvm.eclass
# @MAINTAINER:
# Michał Górny <mgorny@gentoo.org>
# @AUTHOR:
# Michał Górny <mgorny@gentoo.org>
# @SUPPORTED_EAPIS: 6 7 8
# @BLURB: Utility functions to build against slotted LLVM
# @DESCRIPTION:
# The llvm.eclass provides utility functions that can be used to build
# against specific version of slotted LLVM (with fallback to :0 for old
# versions).
#
# This eclass does not generate dependency strings. You need to write
# a proper dependency string yourself to guarantee that appropriate
# version of LLVM is installed.
#
# Example use for a package supporting LLVM 9 to 11:
# @CODE
# inherit cmake llvm
#
# RDEPEND="
#	<sys-devel/llvm-11:=
#	|| (
#		sys-devel/llvm:9
#		sys-devel/llvm:10
#		sys-devel/llvm:11
#	)
# "
# DEPEND=${RDEPEND}
#
# LLVM_MAX_SLOT=11
#
# # only if you need to define one explicitly
# pkg_setup() {
#	llvm_pkg_setup
#	do-something-else
# }
# @CODE
#
# Example for a package needing LLVM+clang w/ a specific target:
# @CODE
# inherit cmake llvm
#
# # note: do not use := on both clang and llvm, it can match different
# # slots then. clang pulls llvm in, so we can skip the latter.
# RDEPEND="
#	>=sys-devel/clang-9:=[llvm_targets_AMDGPU(+)]
# "
# DEPEND=${RDEPEND}
#
# llvm_check_deps() {
#	has_version -d "sys-devel/clang:${LLVM_SLOT}[llvm_targets_AMDGPU(+)]"
# }
# @CODE

case "${EAPI:-0}" in
	0|1|2|3|4|5)
		die "Unsupported EAPI=${EAPI:-0} (too old) for ${ECLASS}"
		;;
	6|7|8)
		;;
	*)
		die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}"
		;;
esac

EXPORT_FUNCTIONS pkg_setup

if [[ ! ${_LLVM_ECLASS} ]]; then

# make sure that the versions installing straight into /usr/bin
# are uninstalled
DEPEND="!!sys-devel/llvm:0"

# @ECLASS_VARIABLE: LLVM_MAX_SLOT
# @DEFAULT_UNSET
# @DESCRIPTION:
# Highest LLVM slot supported by the package. Needs to be set before
# llvm_pkg_setup is called. If unset, no upper bound is assumed.

# @ECLASS_VARIABLE: _LLVM_KNOWN_SLOTS
# @INTERNAL
# @DESCRIPTION:
# Correct values of LLVM slots, newest first.
declare -g -r _LLVM_KNOWN_SLOTS=( {16..8} )

# @FUNCTION: get_llvm_prefix
# @USAGE: [-b|-d] [<max_slot>]
# @DESCRIPTION:
# Find the newest LLVM install that is acceptable for the package,
# and print an absolute path to it.
#
# If -b is specified, the checks are performed relative to BROOT,
# and BROOT-path is returned.  This is appropriate when your package
# calls llvm-config executable.  -b is supported since EAPI 7.
#
# If -d is specified, the checks are performed relative to ESYSROOT,
# and ESYSROOT-path is returned.  This is appropriate when your package
# uses CMake find_package(LLVM).  -d is the default.
#
# If <max_slot> is specified, then only LLVM versions that are not newer
# than <max_slot> will be considered. Otherwise, all LLVM versions would
# be considered acceptable. The function does not support specifying
# minimal supported version -- the developer must ensure that a version
# new enough is installed via providing appropriate dependencies.
#
# If llvm_check_deps() function is defined within the ebuild, it will
# be called to verify whether a particular slot is accepable. Within
# the function scope, LLVM_SLOT will be defined to the SLOT value
# (0, 4, 5...). The function should return a true status if the slot
# is acceptable, false otherwise. If llvm_check_deps() is not defined,
# the function defaults to checking whether sys-devel/llvm:${LLVM_SLOT}
# is installed.
get_llvm_prefix() {
	debug-print-function ${FUNCNAME} "${@}"

	local hv_switch=-d
	while [[ ${1} == -* ]]; do
		case ${1} in
			-b|-d) hv_switch=${1};;
			*) break;;
		esac
		shift
	done

	local prefix=
	if [[ ${EAPI} != 6 ]]; then
		case ${hv_switch} in
			-b)
				prefix=${BROOT}
				;;
			-d)
				prefix=${ESYSROOT}
				;;
		esac
	else
		case ${hv_switch} in
			-b)
				die "${FUNCNAME} -b is not supported in EAPI ${EAPI}"
				;;
			-d)
				prefix=${EPREFIX}
				hv_switch=
				;;
		esac
	fi

	local max_slot=${1}
	local slot
	for slot in "${_LLVM_KNOWN_SLOTS[@]}"; do
		# skip higher slots
		if [[ -n ${max_slot} ]]; then
			if [[ ${max_slot} == ${slot} ]]; then
				max_slot=
			else
				continue
			fi
		fi

		if declare -f llvm_check_deps >/dev/null; then
			local LLVM_SLOT=${slot}
			llvm_check_deps || continue
		else
			# check if LLVM package is installed
			has_version ${hv_switch} "sys-devel/llvm:${slot}" || continue
		fi

		echo "${prefix}/usr/lib/llvm/${slot}"
		return
	done

	# max_slot should have been unset in the iteration
	if [[ -n ${max_slot} ]]; then
		die "${FUNCNAME}: invalid max_slot=${max_slot}"
	fi

	die "No LLVM slot${1:+ <= ${1}} satisfying the package's dependencies found installed!"
}

# @FUNCTION: llvm_fix_clang_version
# @USAGE: <variable-name>...
# @DESCRIPTION:
# Fix the clang compiler name in specified variables to include
# the major version, to prevent PATH alterations from forcing an older
# clang version being used.
llvm_fix_clang_version() {
	debug-print-function ${FUNCNAME} "${@}"

	local shopt_save=$(shopt -p -o noglob)
	set -f
	local var
	for var; do
		local split=( ${!var} )
		case ${split[0]} in
			*clang|*clang++|*clang-cpp)
				local version=()
				read -r -a version < <("${split[0]}" --version)
				local major=${version[-1]%%.*}
				if [[ -n ${major//[0-9]} ]]; then
					die "${var}=${!var} produced invalid --version: ${version[*]}"
				fi

				split[0]+=-${major}
				if ! type -P "${split[0]}" &>/dev/null; then
					die "${split[0]} does not seem to exist"
				fi
				declare -g "${var}=${split[*]}"
				;;
		esac
	done
	${shopt_save}
}

# @FUNCTION: llvm_fix_tool_path
# @USAGE: <variable-name>...
# @DESCRIPTION:
# Fix the LLVM tools referenced in the specified variables to their
# current location, to prevent PATH alterations from forcing older
# versions being used.
llvm_fix_tool_path() {
	debug-print-function ${FUNCNAME} "${@}"

	local shopt_save=$(shopt -p -o noglob)
	set -f
	local var
	for var; do
		local split=( ${!var} )
		local path=$(type -P ${split[0]} 2>/dev/null)
		# if it resides in one of the LLVM prefixes, it's an LLVM tool!
		if [[ ${path} == "${BROOT}/usr/lib/llvm"* ]]; then
			split[0]=${path}
			declare -g "${var}=${split[*]}"
		fi
	done
	${shopt_save}
}

# @FUNCTION: llvm_pkg_setup
# @DESCRIPTION:
# Prepend the appropriate executable directory for the newest
# acceptable LLVM slot to the PATH. For path determination logic,
# please see the get_llvm_prefix documentation.
#
# The highest acceptable LLVM slot can be set in LLVM_MAX_SLOT variable.
# If it is unset or empty, any slot is acceptable.
#
# 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.
llvm_pkg_setup() {
	debug-print-function ${FUNCNAME} "${@}"

	if [[ ${MERGE_TYPE} != binary ]]; then
		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

		local llvm_path=$(get_llvm_prefix "${LLVM_MAX_SLOT}")/bin
		local IFS=:
		local split_path=( ${PATH} )
		local new_path=()
		local x added=

		# prepend new path before first LLVM version found
		for x in "${split_path[@]}"; do
			if [[ ${x} == */usr/lib/llvm/*/bin ]]; then
				if [[ ${x} != ${llvm_path} ]]; then
					new_path+=( "${llvm_path}" )
				elif [[ ${added} && ${x} == ${llvm_path} ]]; then
					# deduplicate
					continue
				fi
				added=1
			fi
			new_path+=( "${x}" )
		done
		# ...or to the end of PATH
		[[ ${added} ]] || new_path+=( "${llvm_path}" )

		export PATH=${new_path[*]}
	fi
}

_LLVM_ECLASS=1
fi