summaryrefslogtreecommitdiff
path: root/eclass/llvm-r2.eclass
blob: 4dda9aedc7c6d30639e2b7ae000304f3a48c515a (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
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
# Copyright 2024 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

# @ECLASS: llvm-r2.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-r2_pkg_setup, llvm_chost_setup, llvm_cbuild_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.
#
# Note that the eclass aims for a best-effort support of CHOST builds
# (i.e. compiling/linking against LLVM) and CBUILD use (i.e. calling
# LLVM tools at build time).  You need to determine what the package
# in question needs, and put the appropriate packages in DEPEND and/or
# BDEPEND appropriately.
#
# Example:
# @CODE
# LLVM_COMPAT=( {16..18} )
#
# inherit llvm-r2
#
# DEPEND="
#   dev-libs/libfoo[${LLVM_USEDEP}]
#   $(llvm_gen_dep '
#     llvm-core/clang:${LLVM_SLOT}=
#     llvm-core/llvm:${LLVM_SLOT}=
#   ')
# "
# @CODE

case ${EAPI} in
	8) ;;
	*) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
esac

if [[ -z ${_LLVM_R2_ECLASS} ]]; then
_LLVM_R2_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=19

# == 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
		# If there is at least one stable slot supported, then enable
		# the newest stable slot by default.
		IUSE="+llvm_slot_${stable[-1]}"
		unset 'stable[-1]'
	else
		# Otherwise, enable the "oldest" ~arch slot.  We really only
		# expect a single ~arch version, so this primarily prevents
		# defaulting to non-keyworded slots.
		IUSE="+llvm_slot_${unstable[0]}"
		unset 'unstable[0]'
	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 '
#     llvm-core/clang:${LLVM_SLOT}=
#     llvm-core/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: generate_llvm_config
# @DESCRIPTION:
# Output a llvm-config compatible script that yields paths specific
# to the requested LLVM version.
generate_llvm_config() {
	debug-print-function ${FUNCNAME} "$@"

	local bindir=$(get_llvm_prefix -b)/bin
	[[ ! -d ${bindir} ]] && bindir=

	local prefix=$(get_llvm_prefix -d)
	local includedir=${prefix}/include
	local libdir=${prefix}/$(get_libdir)
	local cmake_conf=${libdir}/cmake/llvm/LLVMConfig.cmake
	if [[ ! -f ${cmake_conf} ]]; then
		cat <<-EOF
			#!/usr/bin/env sh
			echo "LLVM ${LLVM_SLOT} not installed for ABI=${ABI}" >&2
			exit 127
		EOF
		return
	fi

	local version=$(
		sed -ne 's:set(LLVM_PACKAGE_VERSION \(.*\)):\1:p' "${cmake_conf}" || die
	)
	[[ -n ${version} ]] || die
	local cppdefs=$(
		sed -ne 's:set(LLVM_DEFINITIONS "\(.*\)"):\1:p' "${cmake_conf}" || die
	)
	[[ -n ${cppdefs} ]] || die
	local targets=$(
		sed -ne 's:set(LLVM_TARGETS_TO_BUILD \(.*\)):\1:p' "${cmake_conf}" || die
	)
	[[ -n ${targets} ]] || die
	local libs=$(
		sed -ne 's:set(LLVM_AVAILABLE_LIBS \(.*\)):\1:p' "${cmake_conf}" || die
	)
	[[ -n ${libs} ]] || die
	local target_triple=$(
		sed -ne 's:set(LLVM_TARGET_TRIPLE "\(.*\)"):\1:p' "${cmake_conf}" || die
	)
	[[ -n ${target_triple} ]] || die

	readarray -d';' -t targets <<<"${targets}"
	readarray -d';' -t libs <<<"${libs}"
	# easier than parsing CMake booleans
	local assertions=OFF
	[[ ${cppdefs} == *-D_DEBUG* ]] && assertions=ON
	# major + suffix
	local shlib_name=LLVM-${version%%.*}
	[[ ${version} == *git* ]] && shlib_name+="git${version##*git}"

	local components=(
		"${libs[@]#LLVM}" "${targets[@]}"
		# special component groups (grep for add_llvm_component_group)
		all all-targets engine native nativecodegen
	)

	cat <<-EOF
		#!/usr/bin/env sh

		echo "\${0} \${*}" >> "${T}/llvm-config-calls.txt"

		do_echo() {
			echo "  \${*}" >> "${T}/llvm-config-calls.txt"
			echo "\${@}"
		}

		for arg; do
			case \${arg} in
				--assertion-mode)
					do_echo "${assertions}"
					;;
				--bindir)
					if [ -n "${bindir}" ]; then
						do_echo "${bindir}"
					else
						do_echo "CBUILD LLVM not available" >&2
						exit 1
					fi
					;;
				--build-mode)
					do_echo RelWithDebInfo
					;;
				--build-system)
					do_echo cmake
					;;
				--cflags|--cppflags)
					do_echo "-I${includedir} ${cppdefs[*]}"
					;;
				--cmakedir)
					do_echo "${libdir}/cmake/llvm"
					;;
				--components)
					do_echo "${components[*],,}"
					;;
				--cxxflags)
					do_echo "-I${includedir} -std=c++17 ${cppdefs[*]}"
					;;
				--has-rtti)
					do_echo YES
					;;
				--host-target)
					do_echo "${target_triple}"
					;;
				--ignore-libllvm)
					# ignored
					;;
				--includedir)
					do_echo "${includedir}"
					;;
				--ldflags)
					do_echo "-L${libdir}"
					;;
				--libdir)
					do_echo "${libdir}"
					;;
				--libfiles)
					do_echo "${libdir}/lib${shlib_name}.so"
					;;
				--libnames)
					do_echo lib${shlib_name}.so
					;;
				--libs)
					do_echo "-l${shlib_name}"
					;;
				--link-shared|--link-static)
					# ignored
					;;
				--obj-root|--prefix)
					do_echo "${prefix}"
					;;
				--shared-mode)
					do_echo shared
					;;
				--system-libs)
					do_echo
					;;
				--targets-built)
					do_echo "${targets[*]}"
					;;
				--version)
					do_echo "${version}"
					;;
				-*)
					do_echo "Unsupported option: \${arg}" >&2
					exit 1
					;;
				*)
					# ignore components, we always return the dylib
					;;
			esac
		done
	EOF
}
# @FUNCTION: llvm_cbuild_setup
# @DESCRIPTION:
# Prepend the PATH for selected LLVM version in CBUILD.
#
# This function is meant to be used when the package in question uses
# LLVM tools at build time.  It is called automatically
# by llvm-r2_pkg_setup if LLVM is found installed in BROOT.
#
# Note that llvm-config from this path must not be used to build against
# LLVM, as that will break cross-compilation.
llvm_cbuild_setup() {
	debug-print-function ${FUNCNAME} "$@"

	local broot_prefix=$(get_llvm_prefix -b)
	einfo "Using ${broot_prefix} for CBUILD LLVM ${LLVM_SLOT}"
	[[ -d ${broot_prefix}/bin ]] ||
		die "LLVM ${LLVM_SLOT} not found installed in BROOT (expected: ${broot_prefix}/bin)"

	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
	llvm_prepend_path -b "${LLVM_SLOT}"
}

# @FUNCTION: llvm_chost_setup
# @DESCRIPTION:
# Set the environment for finding selected LLVM slot installed
# for CHOST.  Create llvm-config wrappers to satisfy legacy lookups.
#
# This function is meant to be used when the package in question uses
# LLVM compiles against and links to LLVM.  It is called automatically
# by llvm-r2_pkg_setup if LLVM is found installed in ESYSROOT.
#
# Note that the generated llvm-config may refer to CBUILD installation
# of LLVM via --bindir, if it is found available.
llvm_chost_setup() {
	debug-print-function ${FUNCNAME} "$@"

	local esysroot_prefix=$(get_llvm_prefix -d)
	einfo "Using ${esysroot_prefix} for CHOST LLVM ${LLVM_SLOT}"
	[[ -d ${esysroot_prefix} ]] ||
		die "LLVM ${LLVM_SLOT} not found installed in ESYSROOT (expected: ${esysroot_prefix})"

	# satisfies find_package() in CMake
	export LLVM_ROOT="${esysroot_prefix}"
	export Clang_ROOT="${esysroot_prefix}"
	export LLD_ROOT="${esysroot_prefix}"
	export MLIR_ROOT="${esysroot_prefix}"
	export Polly_ROOT="${esysroot_prefix}"

	# satisfies llvm-config calls, e.g. from meson
	export PATH="${T}/llvm-bin:${PATH}"
	mkdir "${T}"/llvm-bin || die
	# we need to generate it per-ABI, since libdir changes
	local ABI
	for ABI in $(get_all_abis); do
		local path="${T}/llvm-bin/$(get_abi_CHOST)-llvm-config"
		generate_llvm_config > "${path}" || die
		chmod +x "${path}" || die
	done
	ln -s "$(get_abi_CHOST)-llvm-config" "${T}/llvm-bin/llvm-config" || die
}

# @FUNCTION: llvm-r2_pkg_setup
# @DESCRIPTION:
# Handle all supported setup actions automatically.  If LLVM is found
# installed for CBUILD, call llvm_cbuild_setup.  If it is found
# installed for CHOST, call llvm_chost_setup.
#
# This function is a no-op when installing a binary package.
#
# Note that this function is not exported if LLVM_OPTIONAL is set.
# In that case, it needs to be called manually.
llvm-r2_pkg_setup() {
	debug-print-function ${FUNCNAME} "$@"

	if [[ ${MERGE_TYPE} != binary ]]; then
		[[ -z ${LLVM_SLOT} ]] && die "LLVM_SLOT unset (broken USE_EXPAND?)"

		if [[ -d $(get_llvm_prefix -b)/bin ]]; then
			llvm_cbuild_setup
		fi

		if [[ -d $(get_llvm_prefix -d) ]]; then
			llvm_chost_setup
		fi
	fi
}

fi

if [[ ! ${LLVM_OPTIONAL} ]]; then
	EXPORT_FUNCTIONS pkg_setup
fi