aboutsummaryrefslogtreecommitdiff
blob: 5c7d3f97acd74dfd61a43d7c6f7d542cd4206cdc (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
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
# @ECLASS: dlang.eclass
# @MAINTAINER:
# Marco Leise <marco.leise@gmx.de>
# @SUPPORTED_EAPIS: 6 7 8
# @BLURB: install D libraries in multiple locations for each D version and compiler
# @DESCRIPTION:
# The dlang eclass faciliates creating dependiencies on D libraries for use
# with different D compilers and D versions.
# DLANG_VERSION_RANGE can be set in the ebuild to limit the search on the known
# compatible Dlang front-end versions. It is a space separated list with each item
# can be a single version, an open or a closed range (e.g. "2.063 2.065-2.067 2.070-").
# The range can be open in either direction.
# DLANG_PACKAGE_TYPE determines whether the current ebuild can be compiled for
# multiple Dlang compilers (i.e. is a library to be installed for different
# versions of dmd, gdc or ldc2) or a single compiler (i.e. is an application).
# "single" - An application is built and the package will be built using one compiler.
# "multi" - A library is built and multiple compiler versions and vendors can be selected.
# "dmd" - Special case for dmd, which is like "single", but picking no compiler USE flag
#         is allowed and results in a self-hosting dmd.
# DLANG_USE_COMPILER, if set, inhibits the generation of IUSE, REQUIRED_USE, DEPEND
# and RDEPEND for Dlang compilers based on above variables. The ebuild is responsible
# for providing them as required by the function it uses from this eclass.

# @ECLASS_VARIABLE: DLANG_COMPILER_USE
# @DESCRIPTION:
# Holds the active Dlang compiler for an application as a USE flag to be passed on to depencencies (libraries).
# Using this variable one can ensure that all required libraries must be compiled with the same compiler.

# @ECLASS_VARIABLE: DLANG_IMPORT_DIR
# @DESCRIPTION:
# The path that is used to install include files. A sub-directory specific to the package should be used.

# @ECLASS_VARIABLE: DLANG_COMPILER_DISABLED_BACKENDS
# @PRE_INHERIT
# @DEFAULT_UNSET
# @DESCRIPTION:
# Optional list of D compiler backends to disable as a Bash array.
# Possible values include dmd, ldc2, and gdc.
# Set before inheritting the eclass.

if [[ ${_ECLASS_ONCE_DLANG} != "recur -_+^+_- spank" ]] ; then
_ECLASS_ONCE_DLANG="recur -_+^+_- spank"

case ${EAPI:-0} in
	6) inherit eapi7-ver ;;
	7|8) ;;
	*) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
esac

inherit flag-o-matic dlang-compilers
if [[ "${DLANG_PACKAGE_TYPE}" == "multi" ]]; then
	# We handle a multi instance package.
	inherit multilib-minimal
fi

EXPORT_FUNCTIONS src_prepare src_configure src_compile src_test src_install

# Definition of know compilers and supported front-end versions from dlang-compilers.eclass
dlang-compilers_declare_versions

# @FUNCTION: dlang_foreach_config
# @DESCRIPTION:
# Function that calls its arguments for each D configuration. A few environment
# variables will be set for each call:
# ABI: See 'multilib_get_enabled_abis' from multilib-build.eclass.
# MODEL: This is either 32 or 64.
# DLANG_VENDOR: Either DigitalMars, GNU or LDC.
# DC: D compiler command. E.g.
#   /usr/x86_64-pc-linux-gnu/gcc-bin/12/x86_64-pc-linux-gnu-gdc,
#   /usr/lib/dmd/2.067/bin/dmd, or
#   /usr/lib/ldc2/0.17/bin/ldc2
# DMD: DMD compiler command. E.g.
#   /usr/x86_64-pc-linux-gnu/gcc-bin/12/x86_64-pc-linux-gnu-gdmd,
#   /usr/lib/dmd/2.086/bin/dmd, or
#   /usr/lib/ldc2/0.17/bin/ldmd2
# DC_VERSION: Release version of the compiler. This is the version excluding any
#   Patch releases. So dmd 2.064.2 would still be 2.064. This version is used
#   to separate potentially incompatible ABIs and to create the library path.
#   Typical versions of gdc or ldc are 4.8.1 or 0.12.
# DLANG_VERSION: This differs from DC_VERSION in so far as it displays the
#   front-end or language specification version for every compiler. Since the
#   release of D1 it follows the scheme x.yyy and is as of writing at 2.064.
# DLANG_LINKER_FLAG: The command-line flag, the respective compiler understands
#   as a prefix for a single argument that should be passed to the linker.
#   dmd: -L, gdc: -Xlinker, ldc: -L=
# DLANG_LIB_DIR: The compiler and compiler version specific library directory.
dlang_foreach_config() {
	debug-print-function ${FUNCNAME} "${@}"

	local MULTIBUILD_VARIANTS=($(_dlang_build_configurations))

	multibuild_wrapper() {
		debug-print-function ${FUNCNAME} "${@}"

		# We need to reset CC, else when dmd calls it, the result is:
		# "x86_64-pc-linux-gnu-gcc -m32": No such file or directory
		if [[ -v CC ]]; then
			local _ORIGINAL_CC="${CC}"
		fi
		multilib_toolchain_setup "${ABI}"
		if [[ -v _ORIGINAL_CC ]]; then
			CC="${_ORIGINAL_CC}"
		else
			unset CC
		fi
		mkdir -p "${BUILD_DIR}" || die
		pushd "${BUILD_DIR}" >/dev/null || die
		_dlang_use_build_vars "${@}"
		popd >/dev/null || die
	}

	multibuild_foreach_variant multibuild_wrapper "${@}"
}

# @FUNCTION: dlang_single_config
# @DESCRIPTION:
# Wrapper for build phases when only a single build configuraion is used. See `dlang_foreach_config()` for more details.
dlang_single_config() {
	debug-print-function ${FUNCNAME} "${@}"

	local MULTIBUILD_VARIANT=$(_dlang_build_configurations)

	_dlang_use_build_vars "${@}"
}

export DLANG_IMPORT_DIR="usr/include/dlang"

# @FUNCTION: dlang_src_prepare
# @DESCRIPTION:
# Create a single copy of the package sources for each enabled D configuration.
dlang_src_prepare() {
	debug-print-function ${FUNCNAME} "${@}"

	default_src_prepare

	if [[ "${DLANG_PACKAGE_TYPE}" == "multi" ]]; then
		local MULTIBUILD_VARIANTS=($(_dlang_build_configurations))
		multibuild_copy_sources
	fi
}

dlang_src_configure() {
	_dlang_phase_wrapper configure
}

dlang_src_compile() {
	_dlang_phase_wrapper compile
}

dlang_src_test() {
	_dlang_phase_wrapper test
}

dlang_src_install() {
	_dlang_phase_wrapper install
}

# @FUNCTION: dlang_exec
# @DESCRIPTION:
# Run and print a shell command. Aborts the ebuild on error using "die".
dlang_exec() {
	echo "${@}"
	${@} || die
}

# @FUNCTION: dlang_compile_bin
# @DESCRIPTION:
# Compiles a D application. The first argument is the output file name, the
# other arguments are source files. Additional variables can be set to fine tune
# the compilation. They will be prepended with the proper flags for each
# compiler:
# versions - a list of versions to activate during compilation
# imports - a list of import paths
# string_imports - a list of string import paths
#
# Aditionally, if the ebuild offers the "debug" use flag, we will automatically
# raise the debug level to 1 during compilation.
dlang_compile_bin() {
	[[ "${DLANG_PACKAGE_TYPE}" == "multi" ]] && die "${FUNCTION} does not work with DLANG_PACKAGE_TYPE=\"multi\" currently."

	local binname="${1}"
	local sources="${@:2}"

	dlang_exec ${DC} ${DCFLAGS} ${sources} $(_dlang_additional_flags) \
		${LDFLAGS} ${DLANG_OUTPUT_FLAG}${binname}
}

# @FUNCTION: dlang_compile_lib_a
# @DESCRIPTION:
# Compiles a D static library. The first argument is the output file name, the
# other arguments are source files. Additional variables and the
# "debug" use flag will be handled as described in dlang_compile_bin().
dlang_compile_lib_a() {
	local libname="${1}"
	local sources="${@:2}"

	if [[ "${DLANG_VENDOR}" == "GNU" ]]; then
		die "Static libraries for GDC is not supported yet."
	fi
	if [[ "${DLANG_PACKAGE_TYPE}" == "multi" ]]; then
		DCFLAGS="${DCFLAGS} -m${MODEL}"
	fi
	dlang_exec ${DC} ${DCFLAGS} ${sources} $(_dlang_additional_flags) \
		${LDFLAGS} ${DLANG_A_FLAGS} ${DLANG_OUTPUT_FLAG}${libname}
}

# @FUNCTION: dlang_compile_lib_so
# @DESCRIPTION:
# Compiles a D shared library. The first argument is the output file name, the
# second argument is the soname (typically file name without patch level
# suffix), the other arguments are source files. Additional variables and the
# "debug" use flag will be handled as described in dlang_compile_bin().
dlang_compile_lib_so() {
	local libname="${1}"
	local soname="${2}"
	local sources="${@:3}"

	dlang_exec ${DC} ${DCFLAGS} -m${MODEL} ${sources} $(_dlang_additional_flags) \
		${LDFLAGS} ${DLANG_SO_FLAGS} ${DLANG_LINKER_FLAG}-soname=${soname} \
		${DLANG_OUTPUT_FLAG}${libname}
}

# @FUNCTION: dlang_convert_ldflags
# @DESCRIPTION:
# Makes linker flags meant for GCC understandable for the current D compiler.
# Basically it replaces -L with what the D compiler uses as linker prefix.
dlang_convert_ldflags() {
	if [[ "${DLANG_VENDOR}" == "DigitalMars" ]] || [[ "${DLANG_VENDOR}" == "LDC" ]]; then
		local set prefix flags=()
		if [[ is_dmd ]]; then
			prefix="-L"
		elif [[ is_ldc ]]; then
			prefix="-L="
		fi
		for set in ${LDFLAGS}; do
			if [[ "${set:0:4}" == "-Wl," ]]; then
				set=${set/-Wl,/${prefix}}
				flags+=(${set//,/ ${prefix}})
			elif [[ "${set:0:9}" == "-Xlinker " ]]; then
				flags+=(${set/-Xlinker /${prefix}})
			elif [[ "${set:0:2}" == "-L" ]]; then
				flags+=(${set/-L/${prefix}})
			else
				flags+=(${set})
			fi
		done
		echo "${flags[@]}"
	elif [[ "${DLANG_VENDOR}" == "GNU" ]]; then
		echo "${LDFLAGS}"
	else
		die "Set DLANG_VENDOR to DigitalMars, LDC or GNU prior to calling ${FUNCNAME}()."
	fi
}

# @FUNCTION: dlang_dmdw_dcflags
# @DESCRIPTION:
# Convertes compiler specific $DCFLAGS to something that can be passed to the
# dmd wrapper of said compiler. Calls `die` if the flags could not be
# converted.
dlang_dmdw_dcflags() {
	if [[ "${DLANG_VENDOR}" == "DigitalMars" ]]; then
		# There's no translation that needs to be done.
		echo "${DCFLAGS}"
	elif [[ "${DLANG_VENDOR}" == "LDC" ]]; then
		# ldmd2 passes all the arguments that it doesn't understand to ldc2.
		echo "${DCFLAGS}"
	elif [[ "${DLANG_VENDOR}" == "GNU" ]]; then
		# From `gdmd --help`:   -q,arg1,...    pass arg1, arg2, etc. to gdc
		if [[ "${DCFLAGS}" =~ .*,.* ]]; then
			eerror "DCFLAGS (${DCFLAGS}) contain a comma and can not be passed to gdmd."
			eerror "Please remove the comma, use a different compiler, or call gdc directly."
			die "DCFLAGS contain an unconvertable comma."
		fi

		local set flags=()
		for set in ${DCFLAGS}; do
			flags+=("-q,${set}")
		done
		echo "${flags[@]}"
	else
		die "Set DLANG_VENDOR to DigitalMars, LDC or GNU prior to calling ${FUNCNAME}()."
	fi
}

# @FUNCTION: dlang_system_imports
# @DESCRIPTION:
# Returns a list of standard system import paths (one per line) for the current
# D compiler. This includes druntime and Phobos as well as compiler specific
# paths.
dlang_system_imports() {
	if [[ "${DLANG_VENDOR}" == "DigitalMars" ]]; then
		echo "/usr/lib/dmd/${DC_VERSION}/import"
	elif [[ "${DLANG_VENDOR}" == "GNU" ]]; then
		# gcc's SLOT is its major version component.
		echo "/usr/lib/gcc/${CHOST_default}/${DC_VERSION}/include/d"
	elif [[ "${DLANG_VENDOR}" == "LDC" ]]; then
		echo "/usr/lib/ldc2/${DC_VERSION}/include/d"
		echo "/usr/lib/ldc2/${DC_VERSION}/include/d/ldc"
	else
		die "Could not detect D compiler vendor!"
	fi
}

# @FUNCTION: dlang_phobos_level
# @DESCRIPTION:
# Succeeds if we are compiling against a version of Phobos that is at least as
# high as the argument.
dlang_phobos_level() {
	if [ -z "$DLANG_VERSION" ]; then
		[ "$DLANG_PACKAGE_TYPE" != "multi" ] || die "'dlang_phobos_level' needs 'DLANG_PACKAGE_TYPE != multi' when called outside of compiles."
		local config=`_dlang_build_configurations`
		local dc="$(echo ${config} | cut -d- -f2)"
		local dc_version="$(echo ${config} | cut -d- -f3)"
		local DLANG_VERSION="$(_dlang_compiler_to_dlang_version ${dc} ${dc_version})"
	fi
	ver_test "$DLANG_VERSION" -ge "$1"
}

### Non-public helper functions ###

declare -a _dlang_compiler_iuse
declare -a _dlang_compiler_iuse_mask
declare -a _dlang_depends

# @FUNCTION: _dlang_compiler_masked_archs_for_version_range
# @DESCRIPTION:
# Given a Dlang compiler represented through an IUSE flag (e.g. "ldc2-1_1")
# and DEPEND atom (e.g. "dev-lang/ldc2:1.1="), this function tests if the
# current ebuild can depend and thus be compiled with that compiler on
# one or more architectures.
# A compiler that is less stable than the current ebuild for all
# architectures, is dropped completely. A compiler that disqualifies
# for only some, but not all architectures, on the other hand, is disabled
# though REQUIRED_USE (e.g. "!amd64? ( ldc2-1_1? ( dev-lang/ldc2:1.1= ) )").
# Available compilers are accumulated in the _dlang_compiler_iuse array,
# which is later turned into the IUSE variable.
# Partially available compilers are additionally masked out for particular
# architectures by adding them to the _dlang_compiler_iuse_mask array,
# which is later appended to REQUIRED_USE.
# Finally, the _dlang_depends array receives the USE-flag enabled
# dependencies on Dlang compilers, which is later turned into DEPEND and
# RDEPEND.
_dlang_compiler_masked_archs_for_version_range() {
	local iuse=$1
	if [[ "$iuse" == gdc* ]]; then
		local depend="$iuse? ( $2 dev-util/gdmd:${iuse#gdc-} )"
	else
		local depend="$iuse? ( $2 )"
	fi
	local dlang_version=${3%% *}
	local compiler_keywords=${3:${#dlang_version}}
	local compiler_keyword package_keyword arch
	local -a masked_archs

	# Check the version range
	if [[ -n "$4" ]]; then
		[[ $((10#${dlang_version#*.})) -lt $((10#${4#*.})) ]] && return 1
	fi
	if [[ -n "$5" ]]; then
		[[ $((10#${dlang_version#*.})) -gt $((10#${5#*.})) ]] && return 1
	fi

	# Check the stability requirements
	local package_stab comp_stab=0 have_one=0
	for package_keyword in $KEYWORDS; do
		if [ "${package_keyword:0:1}" == "-" ]; then
			# Skip "-arch" and "-*"
			continue
		elif [ "${package_keyword:0:1}" == "~" ]; then
			package_stab=1
			arch=${package_keyword:1}
		else
			package_stab=2
			arch=$package_keyword
		fi

		comp_stab=0
		for compiler_keyword in $compiler_keywords; do
			if [ "$compiler_keyword" == "~$arch" ]; then
				comp_stab=1
			elif [ "$compiler_keyword" == "$arch" ]; then
				comp_stab=2
			fi
		done
		if [ $comp_stab -lt $package_stab ]; then
			masked_archs+=( $arch )
		fi
		if [ $comp_stab -gt 0 ]; then
			have_one=1
		fi
	done
	[ $have_one -eq 0 ] && return 1

	_dlang_compiler_iuse+=( $iuse )
	if [ "${#masked_archs[@]}" -ne 0 ]; then
		for arch in ${masked_archs[@]}; do
			_dlang_compiler_iuse_mask+=( "${arch}? ( !${iuse} )" )
			depend="!${arch}? ( ${depend} )"
		done
	fi
	_dlang_depends+=( "$depend" )
}

# @FUNCTION: _dlang_filter_compilers
# @DESCRIPTION:
# Given a range of Dlang front-end version that the current ebuild can be built with, this function goes through each
# compatible Dlang compilers as provided by the file dlang-compilers.eclass and then calls
# _dlang_compiler_masked_archs_for_version_range where they will be further scrutinized for architecture stability
# requirements and then either dropped as option or partially masked.
_dlang_filter_compilers() {
	local dc_version mapping iuse depend

	if _dlang_compiler_backend_is_enabled "dmd"; then
		# filter for DMD (hardcoding support for x86 and amd64 only)
		for dc_version in "${!_dlang_dmd_frontend[@]}"; do
			mapping="${_dlang_dmd_frontend[${dc_version}]}"
			iuse="dmd-$(ver_rs 1- _ $dc_version)"
			if [ "${DLANG_PACKAGE_TYPE}" == "multi" ]; then
				depend="[${MULTILIB_USEDEP}]"
			else
				depend=""
			fi
			depend="dev-lang/dmd:$dc_version=$depend"
			_dlang_compiler_masked_archs_for_version_range "$iuse" "$depend" "$mapping" "$1" "$2"
		done
	fi

	if _dlang_compiler_backend_is_enabled "gdc"; then
		# GDC (doesn't support sub-slots, to stay compatible with upstream GCC)
		for dc_version in "${!_dlang_gdc_frontend[@]}"; do
			mapping="${_dlang_gdc_frontend[${dc_version}]}"
			iuse="gdc-${dc_version}"
			depend="sys-devel/gcc:$dc_version[d,-d-bootstrap(-)]"
			_dlang_compiler_masked_archs_for_version_range "$iuse" "$depend" "$mapping" "$1" "$2"
		done
	fi

	if _dlang_compiler_backend_is_enabled "ldc2"; then
		# filter for LDC2
		for dc_version in "${!_dlang_ldc2_frontend[@]}"; do
			mapping="${_dlang_ldc2_frontend[${dc_version}]}"
			iuse=ldc2-$(ver_rs 1- _ $dc_version)
			if [ "${DLANG_PACKAGE_TYPE}" == "multi" ]; then
				depend="[${MULTILIB_USEDEP}]"
			else
				depend=""
			fi
			depend="dev-lang/ldc2:$dc_version=$depend"
			_dlang_compiler_masked_archs_for_version_range "$iuse" "$depend" "$mapping" "$1" "$2"
		done
	fi
}

# @FUNCTION: _dlang_filter_versions
# @DESCRIPTION:
# This function sets up the preliminary REQUIRED_USE, DEPEND and RDEPEND ebuild
# variables with compiler requirements for the current ebuild.
# If DLANG_VERSION_RANGE is set in the ebuild, this variable will be parsed to
# limit the search on the known compatible Dlang front-end versions.
# DLANG_PACKAGE_TYPE determines whether the current ebuild can be compiled for
# multiple Dlang compilers (i.e. is a library to be installed for different
# versions of dmd, gdc or ldc2) or a single compiler (i.e. is an application).
_dlang_filter_versions() {
	local range start stop matches d_version versions do_start
	local -A valid

	# Use given range to create a positive list of supported D versions
	if [[ -v DLANG_VERSION_RANGE ]]; then
		for range in $DLANG_VERSION_RANGE; do
			# Define start and stop of range
			if [[ "${range}" == *?- ]]; then
				start="${range%-}"
				stop=
			elif [[ "${range}" == -?* ]]; then
				start=
				stop="${range#-}"
			elif [[ "${range}" == *?-?* ]]; then
				start="${range%-*}"
				stop="${range#*-}"
			else
				start="${range}"
				stop="${range}"
			fi
			_dlang_filter_compilers "$start" "$stop"
		done
	else
		_dlang_filter_compilers "" ""
	fi

	[ ${#_dlang_compiler_iuse[@]} -eq 0 ] && die "No Dlang compilers found that satisfy this package's version range: $DLANG_VERSION_RANGE"

	if [ "${DLANG_PACKAGE_TYPE}" != "multi" ]; then
		REQUIRED_USE="^^"
	else
		REQUIRED_USE="||"
	fi
	DEPEND="${_dlang_depends[@]}"
	# DMD, is statically linked and does not have its host compiler as a runtime dependency.
	if [ "${DLANG_PACKAGE_TYPE}" == "dmd" ]; then
		IUSE="${_dlang_compiler_iuse[@]} +selfhost"
		_dlang_compiler_iuse+=( selfhost )
	else
		RDEPEND="$DEPEND"
		IUSE="${_dlang_compiler_iuse[@]}"
	fi
	REQUIRED_USE="${REQUIRED_USE} ( ${_dlang_compiler_iuse[@]} ) ${_dlang_compiler_iuse_mask[@]}"

	local -a compiler
	for compiler in ${_dlang_compiler_iuse[@]}; do
		DLANG_COMPILER_USE="${DLANG_COMPILER_USE}${compiler}?,"
	done
	DLANG_COMPILER_USE="${DLANG_COMPILER_USE:0:-1}"
}

# @FUNCTION: _dlang_phase_wrapper
# @DESCRIPTION:
# A more or less typical ebuild phase wrapper that invokes `d_src_*` phases if defined in an ebuild or calls Portage's
# default implementation otherwise. when multiple build configurations are provided it also takes care of invoking
# phases once for each configuration as well as calling `d_src_*_all` if defined after the phase has been run for each
# individual build configuration.
_dlang_phase_wrapper() {
	dlang_phase() {
		if declare -f d_src_${1} >/dev/null ; then
			d_src_${1}
		else
			default_src_${1}
		fi
	}

	if [[ "${DLANG_PACKAGE_TYPE}" == "multi" ]]; then
		dlang_foreach_config dlang_phase "${1}"
		# Handle any compiler & arch independent installation steps
		if declare -f d_src_${1}_all >/dev/null ; then
			d_src_${1}_all
		fi
	else
		dlang_single_config dlang_phase "${1}"
	fi
}

# @FUNCTION: _dlang_compiler_to_dlang_version
# @DESCRIPTION:
# Given two arguments, a compiler vendor and a compiler version, this function retrieves the corresponding supported
# Dlang front-end (language) version. This is used in filtering compilers offering incompatible language versions for
# some packages. The resulting language version is echo'd.
_dlang_compiler_to_dlang_version() {
	local mapping
	case "$1" in
		"dmd")
			mapping="$2"
		;;
		"gdc")
			mapping=`echo ${_dlang_gdc_frontend[$2]} | cut -f 1 -d " "`
		;;
		"ldc2")
			mapping=`echo ${_dlang_ldc2_frontend[$2]} | cut -f 1 -d " "`
		;;
	esac
	[ -n "${mapping}" ] || die "Could not retrieve dlang version for '$1-$2'."
	echo "${mapping}"
}

# @FUNCTION: _dlang_build_configurations
# @DESCRIPTION:
# Creates a list of all the build configurations we are going to compile. As all Dlang compilers have different ABIs,
# potentially changing from one release to the next, I decided to distinguish the compiler vendor, compiler version and
# architecture. An application typically only uses on build configuration, but libraries like GtkD may be installed for
# many compiler versions as well as in 32-bit and 64-bit at the same time on multilib. The result is echo'd.
_dlang_build_configurations() {
	local variants version_component use_flag use_flags

	if [ -z ${DLANG_USE_COMPILER+x} ]; then
		use_flags="${USE}"
	else
		use_flags="${DLANG_USE_COMPILER}"
	fi
	for use_flag in $use_flags; do
		case ${use_flag} in
			dmd-* | gdc-* | ldc-* | ldc2-*)
				# On the left are possible $use_flag,
				# on the right, the correct $version_component:
				#
				# dmd-2_088              dmd-2.088
				# gdc-12                 gdc-12
				# gdc-11                 gdc-11
				# ldc-1_29               ldc-1.29
				# ldc2-1_30              ldc2-1.30
				#
				# Note: for ldc2 there is an empty separater betwen the 'c' and the '2'.
				if [[ "${use_flag}" =~ ldc2-* ]]; then
					version_component=$(ver_rs 3 . ${use_flag})
				else
					version_component=$(ver_rs 2-3 . ${use_flag})
				fi

				if [ "${DLANG_PACKAGE_TYPE}" == "multi" ]; then
					for abi in $(multilib_get_enabled_abis); do
						variants="${variants} ${abi}-${version_component}"
					done
				else
					variants="${DEFAULT_ABI:-default}-${version_component}"
				fi
				;;
			selfhost)
				if [ "${DLANG_PACKAGE_TYPE}" == "dmd" ]; then
					variants="default-dmd-selfhost"
				fi
				;;
		esac
	done
	if [ -z "${variants}" ]; then
		die "At least one compiler USE-flag must be selected. This should be checked by REQUIRED_USE in this package."
	fi
	echo ${variants}
}

# @FUNCTION: _dlang_use_build_vars
# @DESCRIPTION:
# Sets and exports important environment variables pertaining to the current build configuration.
# LDFLAGS are also converted into their compiler specific equivalents here.
_dlang_use_build_vars() {
	# Now we define some variables and then call the function.
	# LIBDIR_${ABI} is used by the dolib.* functions, that's why we override it per compiler.
	# The original value is exported as LIBDIR_HOST.
	local libdir_var="LIBDIR_${ABI}"
	export LIBDIR_HOST="${!libdir_var}"
	# Save the default pkgconfig path
	if [[ ! -v DLANG_SAVE_PKG_CONFIG_PATH ]]; then
		# Copy the logic from meson.eclass for setting PKG_CONFIG_PATH
		export DLANG_SAVE_PKG_CONFIG_PATH="${PKG_CONFIG_PATH}${PKG_CONFIG_PATH:+:}/usr/share/pkgconfig"
	fi
	if [[ ! -v DLANG_SAVE_PKG_CONFIG_LIBDIR ]]; then
		# either save the value or provide a sane default lest other eclasses get confused.
		# e.g. meson.eclass will set PKG_CONFIG_LIBDIR using $(get_libdir) which won't
		# work properly since we will overwrite $LIBDIR_$ABI
		export DLANG_SAVE_PKG_CONFIG_LIBDIR="${PKG_CONFIG_LIBDIR:-/usr/$(get_libdir)/pkgconfig}"
	fi
	export ABI="$(echo ${MULTIBUILD_VARIANT} | cut -d- -f1)"
	export DC="$(echo ${MULTIBUILD_VARIANT} | cut -d- -f2)"
	export DC_VERSION="$(echo ${MULTIBUILD_VARIANT} | cut -d- -f3)"
	case "${DC:0:3}" in
		"dmd") export DLANG_VENDOR="DigitalMars" ;;
		"gdc") export DLANG_VENDOR="GNU" ;;
		"ldc") export DLANG_VENDOR="LDC" ;;
	esac
	export DLANG_VERSION="$(_dlang_compiler_to_dlang_version ${DC} ${DC_VERSION})"
	case "${ABI}" in
		"default") ;;
		"x86"*)    export MODEL=32 ;;
		*)         export MODEL=64 ;;
	esac
	if [[ "${DLANG_VENDOR}" == "DigitalMars" ]]; then
		if [ "${DC_VERSION}" != "selfhost" ]; then
			export DC="/usr/lib/dmd/${DC_VERSION}/bin/dmd"
			export DMD="${DC}"
		fi
		# "lib" on pure x86, "lib{32,64}" on amd64 (and multilib)
		if has_multilib_profile || [[ "${MODEL}" == "64" ]]; then
			export LIBDIR_${ABI}="../usr/lib/dmd/${DC_VERSION}/lib${MODEL}"
		else
			export LIBDIR_${ABI}="../usr/lib/dmd/${DC_VERSION}/lib"
		fi
		export DCFLAGS="${DMDFLAGS}"
		export DLANG_LINKER_FLAG="-L"
		export DLANG_A_FLAGS="-lib -fPIC"
		export DLANG_SO_FLAGS="-shared -defaultlib=libphobos2.so -fPIC"
		export DLANG_OUTPUT_FLAG="-of"
		export DLANG_VERSION_FLAG="-version"
		export DLANG_UNITTEST_FLAG="-unittest"
	elif [[ "${DLANG_VENDOR}" == "GNU" ]]; then
		# Note that ldc2 expects the compiler name to be 'gdmd', not 'x86_64-pc-linux-gnu-gdmd'.
		# gcc's SLOT is its major version component.
		export DC="/usr/${CHOST_default}/gcc-bin/${DC_VERSION}/${CHOST_default}-gdc"
		export DMD="/usr/${CHOST_default}/gcc-bin/${DC_VERSION}/gdmd"
		if [[ ${DLANG_PACKAGE_TYPE} != multi ]]; then
			# Both single and dmd enter this branch
			export LIBDIR_${ABI}="lib/gcc/${CHOST_default}/${DC_VERSION}"
		else
			if multilib_is_native_abi; then
				export LIBDIR_${ABI}="lib/gcc/${CHOST_default}/${DC_VERSION}"
			else
				export LIBDIR_${ABI}="lib/gcc/${CHOST_default}/${DC_VERSION}/${MODEL}"
			fi
		fi
		export DCFLAGS="${GDCFLAGS} -shared-libphobos"
		export DLANG_LINKER_FLAG="-Xlinker "
		export DLANG_SO_FLAGS="-shared -fpic"
		export DLANG_OUTPUT_FLAG="-o "
		export DLANG_VERSION_FLAG="-fversion"
		export DLANG_UNITTEST_FLAG="-funittest"
	elif [[ "${DLANG_VENDOR}" == "LDC" ]]; then
		export LIBDIR_${ABI}="../usr/lib/ldc2/${DC_VERSION}/lib${MODEL}"
		export DMD="/usr/lib/ldc2/${DC_VERSION}/bin/ldmd2"
		export DC="/usr/lib/ldc2/${DC_VERSION}/bin/ldc2"
		# To allow separate compilation and avoid object file name collisions,
		# we append -op (do not strip paths from source file).
		export DCFLAGS="${LDCFLAGS} -op"
		export DLANG_LINKER_FLAG="-L="
		export DLANG_A_FLAGS="-lib -relocation-model=pic"
		export DLANG_SO_FLAGS="-shared -relocation-model=pic"
		export DLANG_OUTPUT_FLAG="-of="
		export DLANG_VERSION_FLAG="-d-version"
		export DLANG_UNITTEST_FLAG="-unittest"
	else
		die "Could not detect D compiler vendor!"
	fi
	# We need to convert the LDFLAGS, so they are understood by DMD and LDC.
	if [[ "${DLANG_VENDOR}" == "DigitalMars" ]]; then
		# gc-sections breaks executables for some versions of D
		# It works with the gold linker on the other hand
		# See: https://issues.dlang.org/show_bug.cgi?id=879
		[[ "${DLANG_PACKAGE_TYPE}" == "dmd" ]] && local dlang_version=$SLOT || local dlang_version=$DLANG_VERSION
		if ver_test $dlang_version -lt 2.072; then
			if ! ld -v | grep -q "^GNU gold"; then
				filter-flags {-L,-Xlinker,-Wl\,}--gc-sections
			fi
		fi
		# Filter ld.gold ICF flag. (https://issues.dlang.org/show_bug.cgi?id=17515)
		filter-flags {-L,-Xlinker,-Wl\,}--icf={none,all,safe}
	fi

	if [[ "${DLANG_VENDOR}" == "DigitalMars" ]] || [[ "${DLANG_VENDOR}" == "GNU" ]]; then
		# DMD and GDC don't undestand/work with LTO flags
		filter-ldflags -f{no-,}use-linker-plugin -f{no-,}lto -flto=*
	fi
	export LDFLAGS=`dlang_convert_ldflags`

	# Add the compiler specific pkgconfig paths.
	export PKG_CONFIG_PATH="${DLANG_SAVE_PKG_CONFIG_PATH}:/usr/$(get_libdir)/pkgconfig"
	# Technically, this value will stay the same so it's enough to export it once
	# but it's cleaner to keep these 2 variables close together.
	export PKG_CONFIG_LIBDIR="${DLANG_SAVE_PKG_CONFIG_LIBDIR}"

	"${@}"
}

# @FUNCTION: _dlang_prefix_words
# @DESCRIPTION:
# A simple helper function that prefixes the first argument to all other arguments and echo's the resulting line.
_dlang_prefix_words() {
	for arg in ${*:2}; do
		echo -n " $1$arg"
	done
}

# @FUNCTION: _dlang_additional_flags
# @DESCRIPTION:
# This function is internally used by the dlang_compile_bin/lib/so functions. When `debug` is among the enabled IUSE
# flags, this function ensures that a debug build of the Dlang package is produced. It also provides the additional
# flags for any imports and libraries mentioned in an ebuild in the compiler specific syntax. The resulting flags are
# echo'd.
_dlang_additional_flags() {
	# For info on debug use flags see:
	# https://wiki.gentoo.org/wiki/Project:Quality_Assurance/Backtraces#debug_USE_flag
	case "${DLANG_VENDOR}" in
		"DigitalMars")
			local import_prefix="-I"
			local string_import_prefix="-J"
			local debug_flags="-debug"
			;;
		"GNU")
			local import_prefix="-I"
			local string_import_prefix="-J"
			local debug_flags="-fdebug"
			;;
		"LDC")
			local import_prefix="-I="
			local string_import_prefix="-J="
			local debug_flags="-d-debug"
			;;
	esac
	echo $(has debug ${IUSE} && use debug && echo ${debug_flags})\
		$(_dlang_prefix_words "${DLANG_VERSION_FLAG}=" $versions)\
		$(_dlang_prefix_words $import_prefix $imports)\
		$(_dlang_prefix_words $string_import_prefix $string_imports)\
		$(_dlang_prefix_words "${DLANG_LINKER_FLAG}-l" $libs)
}

# @FUNCTION: _dlang_compiler_backend_is_enabled
# @USAGE: _dlang_compiler_backend_is_enabled <backend>
# @RETURN: Truth if the backend is enabled
# @INTERNAL
# @DESCRIPTION:
# Check if the given backend is enabled.
# Possible values for the backend are: dmd, ldc2 and gdc
_dlang_compiler_backend_is_enabled() {
	local disabled_backend
	for disabled_backend in "${DLANG_COMPILER_DISABLED_BACKENDS[@]}"; do
		[[ ${disabled_backend} == ${1} ]] && return 1
	done
	return 0
}

# Setting DLANG_USE_COMPILER skips the generation of USE-flags for compilers
if [ -z ${DLANG_USE_COMPILER+x} ]; then
	set -f; _dlang_filter_versions; set +f
fi

fi