diff options
author | Andrei Horodniceanu <a.horodniceanu@proton.me> | 2024-03-02 18:11:51 +0200 |
---|---|---|
committer | Andrei Horodniceanu <a.horodniceanu@proton.me> | 2024-04-14 01:47:30 +0300 |
commit | 647a7f67006cbc49f7712ed714fa61ab7553d997 (patch) | |
tree | f1e00c9c0845aed2862792096d01efc1e44c5620 /eclass | |
parent | profiles: Add DLANG_SINGLE_TARGET and DLANG_TARGETS use descriptions (diff) | |
download | dlang-647a7f67006cbc49f7712ed714fa61ab7553d997.tar.gz dlang-647a7f67006cbc49f7712ed714fa61ab7553d997.tar.bz2 dlang-647a7f67006cbc49f7712ed714fa61ab7553d997.zip |
dlang-utils.eclass: new eclass
Signed-off-by: Andrei Horodniceanu <a.horodniceanu@proton.me>
Diffstat (limited to 'eclass')
-rw-r--r-- | eclass/dlang-utils.eclass | 1312 | ||||
-rwxr-xr-x | eclass/tests/dlang-utils.sh | 279 |
2 files changed, 1591 insertions, 0 deletions
diff --git a/eclass/dlang-utils.eclass b/eclass/dlang-utils.eclass new file mode 100644 index 0000000..b759435 --- /dev/null +++ b/eclass/dlang-utils.eclass @@ -0,0 +1,1312 @@ +# Copyright 2024 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +# @ECLASS: dlang-utils.eclass +# @MAINTAINER: +# Andrei Horodniceanu <a.horodniceanu@proton.me> +# @AUTHOR: +# Andrei Horodniceanu <a.horodniceanu@proton.me> +# Based on python-utils-r1.eclass by Michał Górny <mgorny@gentoo.org> et al +# with logic taken from dlang.eclass by Marco Leise <marco.leise@gmx.de>. +# @BUGREPORTS: +# Please report bugs via https://github.com/gentoo/dlang/issues +# @VCSURL: https://github.com/gentoo/dlang +# @SUPPORTED_EAPIS: 8 +# @PROVIDES: dlang-compilers-r1 +# @BLURB: Utility functions for packages with Dlang parts. +# @DESCRIPTION: +# A utility eclass providing functions to query Dlang implementations +# and install Dlang libraries. +# +# This eclass does not set any metadata variables nor export any phase +# functions. It can be inherited safely. + +case ${EAPI} in + 8) ;; + *) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;; +esac + +if [[ ! ${_DLANG_UTILS_R1_ECLASS} ]]; then +_DLANG_UTILS_R1_ECLASS=1 + +inherit dlang-compilers-r1 multilib toolchain-funcs + +# @ECLASS_VARIABLE: DMDFLAGS +# @USER_VARIABLE +# @DESCRIPTION: +# Flags that will be passed to dmd implementations during compilation. +# +# Example value: +# @CODE +# -O -release -mcpu=native +# @CODE + +# @ECLASS_VARIABLE: GDCFLAGS +# @USER_VARIABLE +# @DESCRIPTION: +# Flags that will be passed to gdc implementations during compilation. +# +# Example value: +# @CODE +# -O2 -pipe -march=native -frelease +# @CODE + +# @ECLASS_VARIABLE: LDCFLAGS +# @USER_VARIABLE +# @DESCRIPTION: +# Flags that will be passed to ldc2 implementations during compilation. +# +# Example value: +# @CODE +# -O2 -release -mcpu=native +# @CODE + +# @FUNCTION: _dlang_set_impls +# @INTERNAL +# @DESCRIPTION: +# Set two global variables based on DLANG_COMPAT +# +# - _DLANG_SUPPORTED_IMPLS containing valid implementations supported +# by the ebuild (DLANG_COMPAT - dead implementations), +# +# - and _DLANG_UNSUPPORTED_IMPLS containing valid implementations that +# are not supported by the ebuild. +# +# Implementations in both variables are ordered using the pre-defined +# eclass implementation ordering. +# +# This function must be called once in global scope by an eclass +# utilizing DLANG_COMPAT. +_dlang_set_impls() { + if ! declare -p DLANG_COMPAT &>/dev/null; then + die 'DLANG_COMPAT not declared.' + fi + if [[ ${DLANG_COMPAT@a} != *a* ]]; then + die 'DLANG_COMPAT must be an array' + fi + + local supp=() unsupp=() + + local i + for i in "${_DLANG_ALL_IMPLS[@]}"; do + if has "${i}" "${DLANG_COMPAT[@]}"; then + supp+=( "${i}" ) + else + unsupp+=( "${i}" ) + fi + done + + if [[ ! ${supp[@]} ]]; then + die "No supported implementation in DLANG_COMPAT." + fi + + if [[ ${_DLANG_SUPPORTED_IMPLS[@]} ]]; then + # set once already, verify integrity + if [[ ${_DLANG_SUPPORTED_IMPLS[@]} != ${supp[@]} ]]; then + eerror "Supported impls (DLANG_COMPAT) changed between inherits!" + eerror "Before: ${_DLANG_SUPPORTED_IMPLS[*]}" + eerror "Now : ${supp[*]}" + die "_DLANG_SUPPORTED_IMPLS integrity check failed" + fi + if [[ ${_DLANG_UNSUPPORTED_IMPLS[@]} != ${unsupp[@]} ]]; then + eerror "Unsupported impls changed between inherits!" + eerror "Before: ${_DLANG_UNSUPPORTED_IMPLS[*]}" + eerror "Now : ${unsupp[*]}" + die "_DLANG_UNSUPPORTED_IMPLS integrity check failed" + fi + else + _DLANG_SUPPORTED_IMPLS=( "${supp[@]}" ) + _DLANG_UNSUPPORTED_IMPLS=( "${unsupp[@]}" ) + readonly _DLANG_SUPPORTED_IMPLS _DLANG_UNSUPPORTED_IMPLS + fi +} + +# @ECLASS_VARIABLE: DC +# @DEFAULT_UNSET +# @DESCRIPTION: +# The absolute path to the current Dlang compiler. +# +# Example values (each line is a possible value): +# @CODE +# /usr/lib/ldc2/1.36/bin/ldc2 +# /usr/lib/dmd/2.106/bin/dmd +# /usr/x86_64-pc-linux-gnu/gcc-bin/12/gdc +# @CODE + +# @ECLASS_VARIABLE: EDC +# @DEFAULT_UNSET +# @DESCRIPTION: +# The executable name of the current Dlang compiler. +# +# Please note that this names don't necessarily map to actual +# executables in $PATH so there's no guarantee that calling $EDC will +# work. Instead, use $DC or $(dlang_get_dmdw). +# +# Example values (each line is a possible value): +# @CODE +# dmd-2.106 +# ldc2-1.32 +# gdc-12 +# @CODE + +# @ECLASS_VARIABLE: DCFLAGS +# @DEFAULT_UNSET +# @DESCRIPTION: +# The flags the user provided for the current Dlang implementation. +# +# Example value: +# @CODE +# --O2 --release +# @CODE + +# @ECLASS_VARIABLE: DLANG_LDFLAGS +# @DEFAULT_UNSET +# @DESCRIPTION: +# The contents of $LDFLAGS converted to something the current Dlang +# implementation can understand. +# +# Example value: +# @CODE +# -L--as-needed -L-O1 +# @CODE + +# @FUNCTION: dlang_get_dmdw +# @USAGE: [<impl>] +# @DESCRIPTION: +# Obtain and print the full path of the dmd wrapper for the current +# implementation. If no implementation is provided, ${EDC} will be +# used. +dlang_get_dmdw() { + debug-print-function ${FUNCNAME} "${@}" + + _dlang_export "${@}" DMDW + echo "${DMDW}" +} + +# @FUNCTION: dlang_get_dmdw_dcflags +# @USAGE: [<impl>] +# @DESCRIPTION: +# Obtain and print the user flags for the compiler denoted by given +# implementation, in a form that can be passed to the dmd wrapper of the +# same compiler. If no implementation is provided, ${EDC} will be used. +dlang_get_dmdw_dcflags() { + debug-print-function ${FUNCNAME} "${@}" + + _dlang_export "${@}" DMDW_DCFLAGS + echo "${DMDW_DCFLAGS}" +} + +# @FUNCTION: dlang_get_dmdw_ldflags +# @USAGE: [<impl>] +# @DESCRIPTION: +# Obtain and print the contents of $LDFLAGS, converted to what the dmd +# wrapper of the given implementation understands. If no implementation +# is provided, ${EDC} will be used. +dlang_get_dmdw_ldflags() { + debug-print-function ${FUNCNAME} "${@}" + + _dlang_export "${@}" DLANG_DMDW_LDFLAGS + echo "${DLANG_DMDW_LDFLAGS}" +} + +# @FUNCTION: dlang_get_libdir +# @USAGE: [<impl>] +# @DESCRIPTION: +# Obtain and print the path to the library directory of the current +# implementation. If no implementation is provided, ${EDC} will be used. +# +# This function uses $ABI to calculate to result. For packages that +# support multiple abis care must be taken to set $ABI properly _before_ +# calling this function. +dlang_get_libdir() { + debug-print-function ${FUNCNAME} "${@}" + + _dlang_export "${@}" DLANG_LIBDIR + echo "${DLANG_LIBDIR}" +} + +# @FUNCTION: dlang_get_import_dir +# @USAGE: [<impl>] +# @DESCRIPTION: +# Obtain and print the path to the include directory shared across +# implementations. This value doesn't depend on <impl> as it is always: +# @CODE +# /usr/include/dlang +# @CODE +dlang_get_import_dir() { + debug-print-function ${FUNCNAME} "${@}" + + echo "/usr/include/dlang" +} + +# @FUNCTION: dlang_get_fe_version +# @USAGE: [<impl>] +# @DESCRIPTION: +# Obtain and print the frontend version of the given Dlang +# implementation. If no implementation is provided, ${EDC} will be used. +# +# Example: +# @CODE +# dlang_get_fe_version dmd-2.101 # echo 2.101 +# dlang_get_fe_version ldc-1_35 # echo 2.105 +# dlang_get_fe_version gdc-12 # echo 2.100 +# @CODE +dlang_get_fe_version() { + debug-print-function ${FUNCNAME} "${@}" + + _dlang_export "${@}" DLANG_FE_VERSION + echo "${DLANG_FE_VERSION}" +} + +# @FUNCTION: dlang_get_be_version +# @USAGE: [<impl>] +# @DESCRIPTION: +# Obtain and print the backend version of the given Dlang +# implementation. If no implementation is provided, ${EDC} will be used. +# +# Example: +# @CODE +# dlang_get_be_version dmd-2.101 # echo 2.101 +# dlang_get_be_version ldc-1_35 # echo 1.35 +# dlang_get_be_version gdc-12 # echo 12 +# @CODE +dlang_get_be_version() { + debug-print-function ${FUNCNAME} "${@}" + + _dlang_export "${@}" DLANG_BE_VERSION + echo "${DLANG_BE_VERSION}" +} + +# @FUNCTION: dlang_get_linker_flag +# @USAGE: [<impl>] +# @DESCRIPTION: +# Obtain and print the compiler linker flag for the given +# implementation. If no implementation is provided, ${EDC} will be +# used. +dlang_get_linker_flag() { + debug-print-function ${FUNCNAME} "${@}" + + _dlang_export "${@}" DLANG_LINKER_FLAG + echo "${DLANG_LINKER_FLAG}" +} + +# @FUNCTION: dlang_get_model_flag +# @USAGE: [<impl>] +# @DESCRIPTION: +# Obtain and print a flag to be appended to $DCFLAGS to compile for the +# current ABI. If no implementation is provided, ${EDC} will be used. +# +# If not in a multilib profile nothing will be printed. If on amd64/x86 +# multilib, which is the only one supported by the eclass, either -m64 +# or -m32 is printed based on the value of $ABI. +# +# Since all implementations accept the -m* flag the value of <impl> +# doesn't matter. +dlang_get_model_flag() { + debug-print-function ${FUNCNAME} "${@}" + + _dlang_export "${@}" DLANG_MODEL_FLAG + echo "${DLANG_MODEL_FLAG}" +} + +# @FUNCTION: dlang_get_output_flag +# @USAGE: [<impl>] +# @DESCRIPTION: +# Obtain and print the compiler output flag for the given +# implementation. If no implementation is provided, ${EDC} will be +# used. +dlang_get_output_flag() { + debug-print-function ${FUNCNAME} "${@}" + + _dlang_export "${@}" DLANG_OUTPUT_FLAG + echo "${DLANG_OUTPUT_FLAG}" +} + +# @FUNCTION: dlang_get_unittest_flag +# @USAGE: [<impl>] +# @DESCRIPTION: +# Obtain and print the compiler unittest flag for the given +# implementation. If no implementation is provided, ${EDC} will be +# used. +dlang_get_unittest_flag() { + debug-print-function ${FUNCNAME} "${@}" + + _dlang_export "${@}" DLANG_UNITTEST_FLAG + echo "${DLANG_UNITTEST_FLAG}" +} + +# @FUNCTION: dlang_get_version_flag +# @USAGE: [<impl>] +# @DESCRIPTION: +# Obtain and print the compiler version flag for the given +# implementation. If no implementation is provided, ${EDC} will be +# used. +dlang_get_version_flag() { + debug-print-function ${FUNCNAME} "${@}" + + _dlang_export "${@}" DLANG_VERSION_FLAG + echo "${DLANG_VERSION_FLAG}" +} + +# @FUNCTION: dlang_get_wno_error_flag +# @USAGE: [<impl>] +# @DESCRIPTION: +# Obtain and print the compiler flag which turns warnings into messaged +# instead of errors for the given implementation. If no implementation +# is provided, ${EDC} will be used. +dlang_get_wno_error_flag() { + debug-print-function ${FUNCNAME} "${@}" + + _dlang_export "${@}" DLANG_WNO_ERROR_FLAG + echo "${DLANG_WNO_ERROR_FLAG}" +} + +# @FUNCTION: dlang_print_system_import_paths +# @USAGE: [<impl>] +# @DESCRIPTION: +# Print a list of standard import paths, $EPREFIX included, for the +# current Dlang implementation. If no implementation is provided, ${EDC} +# will be used. +# +# The entries are each printed on a separate line. Entries include the +# paths to phobos, druntime and implementation specific directories, if +# any. +dlang_print_system_import_paths() { + debug-print-function ${FUNCNAME} "${@}" + + _dlang_export "${@}" DLANG_SYSTEM_IMPORT_PATHS + echo "${DLANG_SYSTEM_IMPORT_PATHS}" +} + +# @VARIABLE: imports +# @DEFAULT_UNSET +# @DESCRIPTION: +# A space separated list of import paths that dlang_compile_* will add +# to the command line during compilation. +# +# Example usage: +# @CODE +# local imports="mydir/mylib subdir" +# dlang_compile_bin main main.d # dmd -ofmain main.d -Imydir/mylib -Isubdir +# @CODE + +# @VARIABLE: string_imports +# @DEFAULT_UNSET +# @DESCRIPTION: +# A space separated list of string import paths that dlang_compile_* +# will add to the command line during compilation. +# +# Example usage: +# @CODE +# local string_imports="pix more/pix" +# dlang_compile_bin main main.d # dmd -ofmain main.d -Jpix -Jmore/pix +# @CODE + +# @VARIABLE: versions +# @DEFAULT_UNSET +# @DESCRIPTION: +# A space separated list of versions that dlang_compile_* will enable +# during compilation. +# +# Example usage: +# @CODE +# local versions="foo bar" +# dlang_compile_bin main main.d # dmd -ofmain main.d -version=foo -version=bar +# @CODE + +# @VARIABLE: libs +# @DEFAULT_UNSET +# @DESCRIPTION: +# A space separated list of libraries that dlang_compile_* will link with. +# +# Example usage: +# @CODE +# local libs="gtkd gtk" +# dlang_compile_bin main main.d # dmd -ofmain main.d -L-lgtkd -L-lgtk +# @CODE + +# @FUNCTION: dlang_compile_lib.so +# @USAGE: <output> <soname> <args>... +# @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 or arguments +# for the compiler. +# +# Additional variables can be set to fine tune the compilation. +# Check $imports, $string_imports, $versions and $libs. +dlang_compile_lib.so() { + debug-print-function ${FUNCNAME} "${@}" + + local libname="${1}" + local soname="${2}" + local sources="${@:3}" + + # See dlang_compile_bin comment about these variables. + #local DC DCFLAGS DLANG_LDFLAGS + local DLANG_MODEL_FLAG DLANG_LINKER_FLAG DLANG_OUTPUT_FLAG + _dlang_export DLANG_MODEL_FLAG DLANG_LINKER_FLAG DLANG_OUTPUT_FLAG + + # Put these variables here instead of in _dlang_export to not + # complicate it any further. + local so_flags=$(_dlang_echo_implementation_string \ + "${EDC}" \ + "-shared -defaultlib=libphobos2.so -fPIC" \ + "-shared -fpic" \ + "-shared -relocation-model=pic") + + local cmd=( + ${DC} $(_dlang_compile_extra_flags) + ${DLANG_MODEL_FLAG} + ${so_flags} + ${DLANG_LINKER_FLAG}-soname=${soname} + ${DLANG_OUTPUT_FLAG}${libname} + ${sources} + # Put the user flags last + ${DCFLAGS} ${DLANG_LDFLAGS} + ) + + dlang_exec "${cmd[@]}" +} + +# @FUNCTION: dlang_compile_lib.a +# @USAGE: <output> <args>... +# @DESCRIPTION: +# Compiles a D static library. The first argument is the output file +# name, the other arguments are source files or arguments to the +# compiler. +# +# Additional variables can be set to fine tune the compilation. +# Check $imports, $string_imports, $versions and $libs. +dlang_compile_lib.a() { + debug-print-function ${FUNCNAME} "${@}" + + local libname="${1}" + local sources="${@:2}" + + # See dlang_compile_bin comment about these variables. + #local DC DCFLAGS DLANG_LDFLAGS + local DLANG_MODEL_FLAG DLANG_LINKER_FLAG DLANG_OUTPUT_FLAG + _dlang_export DLANG_MODEL_FLAG DLANG_LINKER_FLAG DLANG_OUTPUT_FLAG + + if [[ ${EDC::3} == @(dmd|ldc) ]]; then + # Put these variables here instead of in _dlang_export to not + # complicate it any further. + local a_flags=$(_dlang_echo_implementation_string \ + "${EDC}" \ + "-lib -fPIC" \ + "" \ + "-lib -relocation-model=pic") + + local cmd=( + ${DC} $(_dlang_compile_extra_flags) + ${DLANG_MODEL_FLAG} + ${a_flags} + ${DLANG_OUTPUT_FLAG}${libname} + ${sources} + # Put the user flags last + ${DCFLAGS} ${DLANG_LDFLAGS} + ) + + dlang_exec "${cmd[@]}" + else + # 2 step, first compile, then ar + local tmpFile=${libname}.dlang-eclass.o + local cmd=( + ${DC} $(_dlang_compile_extra_flags) + ${DLANG_MODEL_FLAG} + -c ${DLANG_OUTPUT_FLAG}${tmpFile} + ${sources} + # Put the user flags last + ${DCFLAGS} ${DLANG_LDFLAGS} + ) + dlang_exec "${cmd[@]}" + + cmd=( $(tc-getAR) ${ARFLAGS} rcs ${libname} ${tmpFile} ) + dlang_exec "${cmd[@]}" + fi +} + +# @FUNCTION: dlang_compile_bin +# @USAGE: <output> <args>... +# @DESCRIPTION: +# Compiles a D application. The first argument is the output file name, +# the other arguments are source files or compiler arguments. +# +# Additional variables can be set to fine tune the compilation. +# Check $imports, $string_imports, $versions and $libs. +dlang_compile_bin() { + debug-print-function ${FUNCNAME} "${@}" + local output=${1} sources=${@:2} + + # These should already be set by dlang-r1 or dlang-single + #local DC DCFLAGS DLANG_LDFLAGS + # We don't set them here to support dmd[selfhost] which + # wants to overwrite some of these values. + local DLANG_OUTPUT_FLAG + _dlang_export DLANG_OUTPUT_FLAG + + local cmd=( + ${DC} $(_dlang_compile_extra_flags) + ${DLANG_OUTPUT_FLAG}${output} + ${sources} + # Put the user flags last. + ${DCFLAGS} ${DLANG_LDFLAGS} + ) + + dlang_exec "${cmd[@]}" +} + +# @FUNCTION: dlang_exec +# @USAGE: <cmd>... +# @DESCRIPTION: +# Execute the command passed as arguments, die on failure. +dlang_exec() { + echo "${@}" + ${@} || die +} + +# @FUNCTION: dlang_dolib.so +# @USAGE: <passthrough-args>... +# @DESCRIPTION: +# A dolib.so wrapper that will install the library to the library +# directory of the current Dlang implementation, denoted by ${EDC}. +# +# The `into' destination needs to be `/usr', if you changed it do: +# @CODE +# into /usr +# @CODE +# before running this function. +dlang_dolib.so() { + debug-print-function ${FUNCNAME} "${@}" + + local DLANG_LIBDIR + _dlang_export DLANG_LIBDIR + + local LIBDIR_${ABI}=${DLANG_LIBDIR} + dolib.so "${@}" +} + +# @FUNCTION: dlang_dolib.a +# @USAGE: <passthrough-args>... +# @DESCRIPTION: +# A dolib.a wrapper that will install the library to the library +# directory of the current Dlang implementation, denoted by ${EDC}. +# +# The `into' destination needs to be `/usr', if you changed it before do: +# @CODE +# into /usr +# @CODE +# before running this function. +dlang_dolib.a() { + debug-print-function ${FUNCNAME} "${@}" + + local DLANG_LIBDIR + _dlang_export DLANG_LIBDIR + + local LIBDIR_${ABI}=${DLANG_LIBDIR} + dolib.a "${@}" +} + +# @FUNCTION: dlang_get_dcflags +# @USAGE: [<impl>] +# @DESCRIPTION: +# Obtain and print the flags the user has set up for the given +# implementation. If no implementation is provided, ${EDC} will be used. +# +# See also: $DMDFLAGS, $GDCFLAGS, $LDCFLAGS +dlang_get_dcflags() { + debug-print-function ${FUNCNAME} "${@}" + + _dlang_export "${@}" DCFLAGS + echo "${DCFLAGS}" +} + +# @FUNCTION: dlang_get_ldflags +# @USAGE: [<impl>] +# @DESCRIPTION: +# Obtain and print the contents of $LDFLAGS, converted to what the given +# implementation understands. If no implementation is provided, ${EDC} +# will be used. +dlang_get_ldflags() { + debug-print-function ${FUNCNAME} "${@}" + + _dlang_export "${@}" DLANG_LDFLAGS + echo "${DLANG_LDFLAGS}" +} + +# @FUNCTION: dlang-filter-dflags +# @USAGE: <pattern> <flags> +# @DESCRIPTION: +# Remove particular <flags> from {DMD,GDC,LDC}FLAGS based on +# <pattern> and also from {DMDW_,}DCFLAGS if they are set. +# <flags> accept shell globs. +# +# For the syntax of <pattern> please see _dlang_impl_matches. +# Note that you will probably want to use globbing for the pattern +# and restrict to one of the three compilers, e.g. "dmd*", "gdc*". +# +# Example: +# @CODE +# DMDFLAGS="-a -b -c -d -e -ff" +# dlang-filter-dflags "dmd*" -e +# echo "${DMDFLAGS}" # "-a -b -c -d -ff" +# @CODE +# +# @CODE +# DCFLAGS="-a -b -c -d -e -ff" +# dlang-filter-dflags '*' '-?' +# echo "${DCFLAGS}" # "-ff" +# @CODE +# +# @CODE +# LDCFLAGS='-O -O1 -flag -O3' +# dlang-filter-dflags 'ldc*' '-O*' +# echo "${LDCFLAGS}" # "-flag" +# @CODE +dlang-filter-dflags() { + local pattern="${1}" + shift + + _dlang_verify_patterns "${pattern}" + + # One implementation from each compiler, the + # version doesn't matter. + local impl + for impl in ldc2-1.32 dmd-2.102 gdc-12; do + if _dlang_impl_matches "${impl}" "${pattern}"; then + local flagVar="${impl::3}" # either dmd, gdc or ldc + flagVar="${flagVar^^}FLAGS" # {DMD,GDC,LDC}FLAGS + + # Taken from _filter-var from flag-o-matic.eclass + local x f new=() + for f in ${!flagVar}; do + for x; do + [[ ${f} == ${x} ]] && continue 2 + done + new+=( "${f}" ) + done + + export ${flagVar}="${new[*]}" + fi + done + + # If the flags are set, udpate them. + for v in {DMDW_,}DCFLAGS; do + [[ ${v} ]] && _dlang_export "${v}" + done + + return 0 +} + +# @FUNCTION: _dlang_export +# @USAGE: [<impl>] <variables>... +# @INTERNAL +# @DESCRIPTION: +# Set and export the Dlang implementation-relevant variables passed +# as parameters. +# +# The optional first parameter may specify the requested Dlang +# implementation (either as DLANG_TARGETS value, e.g. dmd-2_106, +# ldc2-1_32, gdc-12, or an EDC one, e.g. dmd-2.106, ldc2-1.32, gdc-12). +# If no implementation has been passed, the current one will be obtained +# from ${EDC}. +# +# Some variables, like DLANG_LIBIDR and DLANG_MODEL_FLAG, are calculated +# based on $ABI. For this reason ebuilds that handle multiple abis +# should handle first the abis then the dlang portions. Shortly: +# @CODE +# multilib_foreach_abi dlang_foreach_impl some_function # good +# dlang_foreach_impl multilib_foreach_abi some_function # bad +# @CODE +# +# Note that there is one more form of <impl> that is accepted. It may be +# in the form "dmd-wrap-<actual_impl>" where <actual_impl> is in the +# form described above. This is only used internally to keep LDFLAGS +# logic in the same place. Be aware of this but don't use it unless +# necessary or it will become hard to keep track of stuff very fast. +_dlang_export() { + debug-print-function ${FUNCNAME} "${@}" + + local impl + case "${1}" in + dmd-*|ldc2-*|gdc-*) + impl=${1/_/.} + shift + ;; + *) + impl=${EDC} + if [[ -z ${impl} ]]; then + die "_dlang_export called without a dlang implementation and EDC is unset" + fi + ;; + esac + debug-print "${FUNCNAME}: implementation: ${impl}" + + local var + for var; do + case "${var}" in + EDC) + export EDC=${impl} + debug-print "${FUNCNAME}: EDC = ${EDC}" + ;; + DC) + export DC=$( + _dlang_echo_implementation_string \ + "${impl}" \ + "${EPREFIX}/usr/lib/dmd/${impl#dmd-}/bin/dmd" \ + "${EPREFIX}/usr/${CHOST_default}/gcc-bin/${impl#gdc-}/gdc" \ + "${EPREFIX}/usr/lib/ldc2/${impl#ldc2-}/bin/ldc2" + ) + # We could have the path, in the case of gdc, be ${CHOST}-gdc but + # that breaks checks like `if(basename(DC) == gdc)` which seem to + # be quite common. For this reason keep the basename gdc. + debug-print "${FUNCNAME}: DC = ${DC}" + ;; + DMDW) + export DMDW=$( + _dlang_echo_implementation_string \ + "${impl}" \ + "${EPREFIX}/usr/lib/dmd/${impl#dmd-}/bin/dmd" \ + "${EPREFIX}/usr/${CHOST_default}/gcc-bin/${impl#gdc-}/gdmd" \ + "${EPREFIX}/usr/lib/ldc2/${impl#ldc2-}/bin/ldmd2" + ) + # Same observation about ${CHOST}-gdmd as above. + debug-print "${FUNCNAME}: DMDW = ${DMDW}" + ;; + DLANG_LIBDIR) + # There are two supported use cases for dlang packages: + # no-multilib profile and amd64/x86 multilib. + pick_nomulti_amd64_x86() { + if ! has_multilib_profile; then + echo "${1}" + else + case "${ABI}" in + amd64*) echo "${2}" ;; + x86*) echo "${3}" ;; + *) + eerror "ABI ${ABI} is not supported in a multilib configuration." + die "Multilib abi is not x86/amd64!" + esac + fi + } + + local libdirname + case "${impl::3}" in + ldc) + # Old dlang.eclass always picked lib<bits> which + # isn't always correct. The proper calculation + # is found in runtime/CMakeLists.txt which is: + # - native abi is always put in lib<LIB_SUFFIX> + # which is set by cmake.eclass to $(get_libdir) + # - x86 on amd64 is put in lib<bits> + libdirname=$(pick_nomulti_amd64_x86 \ + "$(get_libdir)" "$(get_libdir)" "lib32") + ;; + gdc) + # I have no idea how gcc does it but the line + # below gives the correct result, probably. + libdirname=$(pick_nomulti_amd64_x86 "" "" "/32") + ;; + dmd) + # Wonderful old dmd. It only supports x86 and + # amd64 so we only have to consider these two + # arches, either independently or multilib. + # + # The logic is controlled by us so the calculation + # is found in dlang.eclass. Just copy it here, mostly. + # Simplify the ABI usage a little. + local model + case "${ABI}" in + x86*) model=32 ;; + amd64*) model=64 ;; + *) die "Unknown ABI ${ABI} for dmd implementation." ;; + esac + + if has_multilib_profile || [[ ${model} == 64 ]]; then + libdirname=lib${model} + else + libdirname=lib + fi + ;; + esac + + export DLANG_LIBDIR=$( + _dlang_echo_implementation_string \ + "${impl}" \ + "lib/dmd/${impl#dmd-}/" \ + "lib/gcc/${CHOST_default}/${impl#gdc-}" \ + "lib/ldc2/${impl#ldc2-}/" + )${libdirname} + debug-print "${FUNCNAME}: DLANG_LIBDIR = ${DLANG_LIBDIR}" + ;; + DLANG_IMPORT_DIR) + # This is the only variable which is treated + # differently. Since it doesn't depend on <impl> we want + # to allow setting its value even if $EDC is unset. + export DLANG_IMPORT_DIR="$(dlang_get_import_dir)" + debug-print "${FUNCNAME}: DLANG_IMPORT_DIR = ${DLANG_IMPORT_DIR}" + ;; + DLANG_MODEL_FLAG) + if has_multilib_profile; then + # Only x86/amd64 multilib is supported + case "${ABI}" in + x86*) DLANG_MODEL_FLAG=-m32 ;; + amd64*) DLANG_MODEL_FLAG=-m64 ;; + *) die "ABI ${ABI} is not supported in a multilib configuration." + esac + else + DLANG_MODEL_FLAG= + fi + export DLANG_MODEL_FLAG + debug-print "${FUNCNAME}: DLANG_MODEL_FLAG = ${DLANG_MODEL_FLAG}" + ;; + DCFLAGS) + # Old dlang.eclass added -op (do not strip paths from + # source files) to LDCFLAGS. This doesn't seem like + # something that should be toggled unconditionally so it + # is not added here. + # + # Changes in the behavior of packages with this flag + # enabled I've observed in dev-lang/ldc2 where a regex + # match gets messed up though there don't seem to be any + # relevant consequences (observe the `Host D compiler + # linker args' config line). + # + # The old eclass added -shared-libphobos to + # GDCFLAGS. This is quite important but, since it a flag + # that affects linking, it has been moved to DLANG_LDFLAGS. + export DCFLAGS=$( + _dlang_echo_implementation_string \ + "${impl}" "${DMDFLAGS}" "${GDCFLAGS}" "${LDCFLAGS}") + debug-print "${FUNCNAME}: DCFLAGS = ${DCFLAGS}" + ;; + DMDW_DCFLAGS) + # Don't copy the logic from above, just re-call. + local DCFLAGS + _dlang_export "${impl}" DCFLAGS + case "${impl}" in + # dmd understands his own flags and ldmd2 passes + # through unknown flags to ldc2. + dmd*|ldc*) DMDW_DCFLAGS="${DCFLAGS}" ;; + gdc*) + local flags=( ${DCFLAGS} ) + if [[ ${flags[*]} == *,* ]]; then + eerror "gdc-style flags can not be converted to gdmd-style" + eerror "because they contain a comma: ${flags[*]}" + + die 'flags contain a nonconvertible comma.' + fi + # `-arg1' `-arg2' => `-q,-arg1' `-q,-arg2' + DMDW_DCFLAGS="${flags[@]/#/-q,}" + ;; + esac + export DMDW_DCFLAGS + debug-print "${FUNCNAME}: DMDW_DCFLAGS = ${DMDW_DCFLAGS}" + ;; + DLANG_LDFLAGS) + # In case some $LDFLAGS fail with some Dlang + # implementations this is where they should be + # stripped. Out of the old eclass: + # + # --gc-sections, fails until dmd-2.072 + # + # --icf= still has an open bug but I can't reproduce it + # so I won't remove it. I've tested building dub with + # -L--icf=safe with both ld.gold and ld.lld, static and + # dynamic phobos. The old eclass only removed it for dmd. + # See: https://issues.dlang.org/show_bug.cgi?id=17515 + # + # Very important, add -shared-libphobos for gdc. It + # _will_ be linked statically otherwise. + case "${impl::3}" in + dmd|ldc) + # Old dlang.eclass picked -L for dmd and -L= for + # ldc2. Meson doesn't like -L= however so we go + # with -L for both. See: + # 391ce890a1ca37cce3ee643f61c63c06f428d0dc + local prefix=-L + + # Convert -Wl arguments + local flags=() flag + for flag in ${LDFLAGS}; do + if [[ ${flag::4} == -Wl, ]]; then + # -Wl,a,b,c -> -La -Lb -Lc + flag="${prefix}${flag#-Wl,}" # flag="-La,b,c" + flag="${flag//,/ ${prefix}}" # flag="-La -Lb -Lc" + fi + flags+=( "${flag}" ) + done + DLANG_LDFLAGS="${flags[*]}" + + # Then convert -Xlinker + + # This substitution can fail if there is more + # than one space. It's better than the old eclass which + # didn't do it at all (though it tried to). + DLANG_LDFLAGS="${DLANG_LDFLAGS//-Xlinker /${prefix}}" + ;; + gdc) + DLANG_LDFLAGS="${LDFLAGS} -shared-libphobos" + ;; + esac + export DLANG_LDFLAGS + debug-print "${FUNCNAME}: DLANG_LDFLAGS = ${DLANG_LDFLAGS}" + ;; + DLANG_DMDW_LDFLAGS) + # It would be very easy if we could go like in + # DMDW_DCFLAGS and just insert some -q, for gdc. The + # problem is $LDFLAGS typically contain commas (-Wl,-O1) + # so that solution is busted. Because of this we have + # to do an actual conversions. + # + # But a dmd wrapper is close enough to a dmd + # implementation so the logic from DLANG_LDFLAGS should + # suffice, in this case at least. + local DLANG_LDFLAGS + case "${impl::3}" in + dmd|ldc) _dlang_export "${impl}" DLANG_LDFLAGS ;; + gdc) + _dlang_export "dmd-wrap-${impl}" DLANG_LDFLAGS + # Do not forget the very important flag + DLANG_LDFLAGS+=" -q,-shared-libphobos" + ;; + esac + export DLANG_DMDW_LDFLAGS=${DLANG_LDFLAGS} + debug-print "${FUNCNAME}: DLANG_DMDW_LDFLAGS = ${DLANG_DMDW_LDFLAGS}" + ;; + DLANG_LINKER_FLAG) + export DLANG_LINKER_FLAG=$( + _dlang_echo_implementation_string \ + "${impl}" "-L" "-Wl," "-L") + debug-print "${FUNCNAME}: DLANG_LINKER_FLAG = ${DLANG_LINKER_FLAG}" + ;; + DLANG_OUTPUT_FLAG) + export DLANG_OUTPUT_FLAG=$( + _dlang_echo_implementation_string \ + "${impl}" "-of" "-o" "-of=") + debug-print "${FUNCNAME}: DLANG_OUTPUT_FLAG = ${DLANG_OUTPUT_FLAG}" + ;; + DLANG_UNITTEST_FLAG) + export DLANG_UNITTEST_FLAG=$( + _dlang_echo_implementation_string \ + "${impl}" "-unittest" "-funittest" "-unittest") + debug-print "${FUNCNAME}: DLANG_UNITTEST_FLAG = ${DLANG_UNITTEST_FLAG}" + ;; + DLANG_VERSION_FLAG) + export DLANG_VERSION_FLAG=$( + _dlang_echo_implementation_string \ + "${impl}" "-version" "-fversion" "-d-version") + debug-print "${FUNCNAME}: DLANG_VERSION_FLAG = ${DLANG_VERSION_FLAG}" + ;; + DLANG_FE_VERSION) + local implDetails=( $(_dlang_get_impl_details "${impl}") ) + export DLANG_FE_VERSION=${implDetails[1]} + debug-print "${FUNCNAME}: DLANG_FE_VERSION = ${DLANG_FE_VERSION}" + ;; + DLANG_BE_VERSION) + export DLANG_BE_VERSION=${impl#*-} + debug-print "${FUNCNAME}: DLANG_BE_VERSION = ${DLANG_BE_VERSION}" + ;; + DLANG_WNO_ERROR_FLAG) + export DLANG_WNO_ERROR_FLAG=$( + _dlang_echo_implementation_string \ + "${impl}" "-wi" "-Wno-error" "--wi") + debug-print "${FUNCNAME}: DLANG_WNO_ERROR_FLAG = ${DLANG_WNO_ERROR_FLAG}" + ;; + DLANG_SYSTEM_IMPORT_PATHS) + # Basically copy the output of each compiler when they + # can't find a module. + # + # Right now there's only 1 path for each implementation + # but if there were more they need to be separated by + # \n. + # + # Old dlang.eclass added include/d/ldc for ldc2 but that + # doesn't make much sense as the compiler can't import + # the modules in that folder by default. Since the only + # consumer of this variable is dcd which is used for + # autocompletion it's better to go with the rationale + # above, i.e. whatever the compiler finds by default. + export DLANG_SYSTEM_IMPORT_PATHS=$( + _dlang_echo_implementation_string \ + "${impl}" \ + "${EPREFIX}/usr/lib/dmd/${impl#dmd-}/import" \ + "${EPREFIX}/usr/lib/gcc/${CHOST_default}/${impl#gdc-}/include/d" \ + "${EPREFIX}/usr/lib/ldc2/${impl#ldc2-}/include/d" + ) + + debug-print "${FUNCNAME}: DLANG_SYSTEM_IMPORT_PATHS = ${DLANG_SYSTEM_IMPORT_PATHS}" + ;; + DLANG_PKG_DEP) + _dlang_check_DLANG_REQ_USE + local usedep=${DLANG_REQ_USE[${impl%-*}]} + if [[ ${usedep} ]]; then + usedep=$( + _dlang_echo_implementation_string \ + "${impl}" "[${usedep}]" "[d,${usedep}]" "[${usedep}]") + else + usedep=$( + _dlang_echo_implementation_string \ + "${impl}" "" "[d]" "") + fi + + # The eclass guarantees both a Dlang compiler and a dmd + # wrapper of said compiler. ldmd2 comes with + # dev-lang/ldc2 but gdmd has to be installed separately. + # + # dmd and ldc2 should have ABI compatible patch releases + # but we will use :slot= just in case. + export DLANG_PKG_DEP=$( + _dlang_echo_implementation_string \ + "${impl}" \ + "dev-lang/dmd:${impl#dmd-}=${usedep}" \ + "sys-devel/gcc:${impl#gdc-}${usedep} dev-util/gdmd:${impl#gdc-}" \ + "dev-lang/ldc2:${impl#ldc2-}=${usedep}" + ) + debug-print "${FUNCNAME}: DLANG_PKG_DEP = ${DLANG_PKG_DEP}" + ;; + *) + die "_dlang_export: unknown variable ${var}" + esac + done +} + +# @FUNCTION: _dlang_wrapper_setup +# @USAGE: [<path> [<impl>]] +# @INTERNAL +# @DESCRIPTION: +# Create proper dlang executable setup and pkg-config wrapper (if +# available) in the directory named by <path>. Set up PATH and +# PKG_CONFIG_PATH appropriately. <path> defaults to ${T}/${EDC}. +# +# The wrappers will be created for implementation named by <impl>, or +# for one named by ${EDC} if no <impl> is passed. +# +# If the named directory contains a mark file, it will be assumed to +# contain proper wrappers already and only environment setup will be +# done. If wrapper update is requested, the directory shall be removed +# first. +# +# It is important to note that this function uses $DLANG_LIBDIR which +# uses $ABI to be calculated. Make sure that $ABI is set properly +# _before_ this function is called otherwise wrong variables will be +# generated. +_dlang_wrapper_setup() { + debug-print-function ${FUNCNAME} "${@}" + + local workdir=${1:-${T}/${EDC}} + local impl=${2:-${EDC}} + + [[ ${workdir} ]] || die "${FUNCNAME}: no workdir specified." + [[ ${impl} ]] || die "${FUNCNAME}: no impl nor EDC specified." + + local compiler=${impl%-*} + + if [[ ! -x ${workdir}/.dlang_marked ]]; then + mkdir -p "${workdir}" || die + touch "${workdir}"/.dlang_marked || die + + mkdir -p "${workdir}"/bin || die + + # Clean up, in case we were supposed to do a cheap update. We + # have to remove any previous compilers, so no use but to glob. + # We can be more specific, by doing dmd*, ldc2*, gdc* but that's + # too many lines. + rm -f "${workdir}"/bin/* || die + rm -f "${workdir}"/pkgconfig || die + + local EDC DC DLANG_LIBDIR + _dlang_export "${impl}" EDC DC DLANG_LIBDIR + + # Dlang compiler + ln -s "${DC}" "${workdir}/bin/${compiler}" || die + # pkg-config, this may create a broken symlink + ln -s "${EPREFIX}/usr/${DLANG_LIBDIR}/pkgconfig" "${workdir}"/pkgconfig || die + + # dmd and ldc2 use $CC to link so specify it. + tc-export CC + # With dmd $CC is split by spaces, with ldc it is not. See: + # https://github.com/ldc-developers/ldc/pull/4582. We can solve + # this by: + if [[ ${CC} == *' '* ]]; then + # Only make a script if $CC differs from itself when it is + # expanded. + cat > "${workdir}/bin/${CC}" <<EOF +#!/bin/sh +exec ${CC} "\${@}" +EOF + chmod +x "${workdir}/bin/${CC}" + # which creates a script that properly expands $CC that will + # be called when ldc2 tries to link stuff. This is better + # than the old eclass which disregarded this value. + fi + fi + + # Now, set the environment. + # But note that ${workdir} may be shared with something else, + # and thus already on top of PATH. + if [[ ${PATH##:*} != ${workdir}/bin ]]; then + PATH=${workdir}/bin${PATH:+:${PATH}} + fi + if [[ ${PKG_CONFIG_PATH##:*} != ${workdir/pkgconfig} ]]; then + PKG_CONFIG_PATH=${workdir}/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}} + fi + + export PATH PKG_CONFIG_PATH +} + +# @FUNCTION: _dlang_compile_extra_flags +# @INTERNAL +# @DESCRIPTION: +# Generate and echo implementation dependent compiler arguments for: +# +# - import directories, specified by $imports +# +# - string import directories, specified by $string_imports +# +# - version to enable, specified by $versions +# +# - libraries to link, specified by $libs +# +# The implementation is taken from ${EDC}. +_dlang_compile_extra_flags() { + debug-print-function ${FUNCNAME} "${@}" + + local imports=( ${imports} ) + local simports=( ${string_imports} ) + local versions=( ${versions} ) + local libs=( ${libs} ) + + debug-print "${FUNCNAME}: imports: ${imports[*]}" + debug-print "${FUNCNAME}: simports: ${simports[*]}" + debug-print "${FUNCNAME}: versions: ${versions[*]}" + debug-print "${FUNCNAME}: libs: ${libs[*]}" + + local DLANG_VERSION_FLAG DLANG_LINKER_FLAG + _dlang_export DLANG_VERSION_FLAG DLANG_LINKER_FLAG + + # Just like old dlang.eclass, though maybe ldc2 can use -I and -J + local import_prefix simport_prefix + case "${EDC::3}" in + dmd|gdc) + import_prefix=-I + simport_prefix=-J + ;; + ldc) + import_prefix=-I= + simport_prefix=-J= + ;; + esac + + echo \ + "${imports[@]/#/${import_prefix}}" \ + "${simports[@]/#/${simport_prefix}}" \ + "${versions[@]/#/${DLANG_VERSION_FLAG}=}" \ + "${libs[@]/#/${DLANG_LINKER_FLAG}-l}" +} + +# @FUNCTION: _dlang_echo_implementation_string +# @USAGE: <impl> <if-dmd> <if-gdc> <if-ldc2> +# @INTERNAL +# @DESCRIPTION: +# Based on an implementation, echo one of the parameters. +_dlang_echo_implementation_string() { + debug-print-function ${FUNCNAME} "${@}" + + case "${1::3}" in + dmd) echo "${2}" ;; + gdc) echo "${3}" ;; + ldc) echo "${4}" ;; + *) die "Unknown implementation: ${1}." ;; + esac +} + +# @FUNCTION: _dlang_check_DLANG_REQ_USE +# @INTERNAL +# @DESCRIPTION: +# Check the $DLANG_REQ_USE variable and make sure it's in the correct +# format. +# +# More precisely, check that it's an associative array and that it only +# contains the keys: "dmd", "gdc", or "ldc2". +_dlang_check_DLANG_REQ_USE() { + debug-print-function ${FUNCNAME} "${@}" + + ! declare -p DLANG_REQ_USE &>/dev/null && return + [[ ${DLANG_REQ_USE@a} != *A* ]] && die "DLANG_REQ_USE must be an associative array!" + + local key + for key in "${!DLANG_REQ_USE[@]}"; do + case "${key}" in + dmd|gdc|ldc2) ;; + ldc) die "Unknown key ${key} in DLANG_REQ_USE, perhaps you meant ldc2?" ;; + *) die "Unknown key ${key} in DLANG_REQ_USE!" ;; + esac + done +} + +# @FUNCTION: _dlang_impl_matches +# @USAGE: <impl> [<pattern>...] +# @INTERNAL +# @DESCRIPTION: +# Check whether the specified <impl> matches at least one of the +# patterns following it. Return 0 if it does, 1 otherwise. Matches +# if no patterns are provided. +# +# <impl> can be in DLANG_COMPAT or EDC form. The patterns can +# either be fnmatch-style or frontend versions, e.g "2.100". +_dlang_impl_matches() { + [[ ${#} -ge 1 ]] || die "${FUNCNAME}: takes at least 1 parameter" + [[ ${#} -eq 1 ]] && return 0 + + local impl=${1/./_} pattern + shift + + for pattern; do + case ${pattern} in + 2.[0-9][0-9][0-9]) + local DLANG_FE_VERSION + _dlang_export "${impl}" DLANG_FE_VERSION + [[ ${pattern} == ${DLANG_FE_VERSION} ]] && return 0 + ;; + *) + # unify value style to allow lax matching + [[ ${impl} == ${pattern/./_} ]] && return 0 + ;; + esac + done + + return 1 + +} + +# @FUNCTION: _dlang_verify_patterns +# @USAGE: <pattern>... +# @INTERNAL +# @DESCRIPTION: +# Verify whether the patterns passed to the eclass function are correct +# (i.e. can match any valid implementation). Dies on wrong pattern. +_dlang_verify_patterns() { + debug-print-function ${FUNCNAME} "${@}" + + local impl pattern + for pattern; do + case ${pattern} in + # Only check for versions as they appear in + # _DLANG_*_FRONTENDS, not in _DLANG_HISTORICAL_IMPLS. + 2.10[01234567]) + continue + ;; + esac + + for impl in "${_DLANG_ALL_IMPLS[@]}" "${_DLANG_HISTORICAL_IMPLS[@]}" + do + [[ ${impl} == ${pattern/./_} ]] && continue 2 + done + + die "Invalid implementation pattern: ${pattern}" + done +} + +fi diff --git a/eclass/tests/dlang-utils.sh b/eclass/tests/dlang-utils.sh new file mode 100755 index 0000000..70915c3 --- /dev/null +++ b/eclass/tests/dlang-utils.sh @@ -0,0 +1,279 @@ +#!/bin/bash +# Copyright 2024 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +gentooRepo=$(portageq get_repo_path / gentoo) +readonly gentooRepo + + +EAPI=8 +source "${gentooRepo}"/eclass/tests/tests-common.sh || exit +TESTS_ECLASS_SEARCH_PATHS=( .. "${gentooRepo}"/eclass ) + +# Before the inherit so multilib.eclass picks the correct value +export CHOST=x86_my_whatever +inherit dlang-utils + +test_var() { + local var=${1} + local impl=${2} + local expect=${3} + + tbegin "${var} for ${impl}" + + local ${var} + _dlang_export ${impl} ${var} + # We have variables with [] which breaks [[ == ]] + [ "${!var}" = "${expect}" ] || eerror "(${impl}: ${var}: '${!var}' != '${expect}'" + + tend ${?} +} + +test_is() { + local func=${1} + local expect=${2} + + tbegin "${func} (expecting: ${expect})" + + ${func} + [[ ${?} == ${expect} ]] + + tend ${?} +} + + +test_var EDC dmd-2_102 dmd-2.102 +test_var EDC dmd-2.102 dmd-2.102 +test_var EDC gdc-13 gdc-13 +test_var EDC ldc2-1_35 ldc2-1.35 + +test_var DC dmd-2_102 "${EPREFIX}"/usr/lib/dmd/2.102/bin/dmd +test_var DC ldc2-1_36 "${EPREFIX}"/usr/lib/ldc2/1.36/bin/ldc2 +test_var DC gdc-12 "${EPREFIX}"/usr/"${CHOST_default}"/gcc-bin/12/gdc + +test_var DMDW dmd-2.102 "${EPREFIX}"/usr/lib/dmd/2.102/bin/dmd +test_var DMDW ldc2-1.36 "${EPREFIX}"/usr/lib/ldc2/1.36/bin/ldmd2 +test_var DMDW gdc-12 "${EPREFIX}"/usr/"${CHOST_default}"/gcc-bin/12/gdmd + +# DLANG_LIBDIR tested bellow + +test_var DLANG_IMPORT_DIR dmd-2_102 "/usr/include/dlang" +test_var DLANG_IMPORT_DIR gdc-13 "/usr/include/dlang" +test_var DLANG_IMPORT_DIR ldc2-1_35 "/usr/include/dlang" + +# DLANG_MODEL_FLAGS tested alongside DLANG_LIBDIR + +DMDFLAGS=bar +GDCFLAGS=baz +LDCFLAGS=foo + +test_var DCFLAGS dmd-2.102 "bar" +test_var DCFLAGS gdc-13 "baz" +test_var DCFLAGS ldc2-1_36 "foo" + +DMDFLAGS='-O' +GDCFLAGS='-march=native' +LDCFLAGS='-flto' + +test_var DMDW_DCFLAGS dmd-2.102 "-O" +test_var DMDW_DCFLAGS gdc-13 '-q,-march=native' +test_var DMDW_DCFLAGS ldc2-1_36 "-flto" + +LDFLAGS='-Wl,-O1 -Xlinker --as-needed -garbage' +test_var DLANG_LDFLAGS dmd-2.102 "-L-O1 -L--as-needed -garbage" +test_var DLANG_LDFLAGS gdc-13 "${LDFLAGS} -shared-libphobos" +test_var DLANG_LDFLAGS ldc2-1_36 "-L-O1 -L--as-needed -garbage" + +# Test multiple flags chained in -Wl, +LDFLAGS='-Wl,-z,pack-relative-relocs -Wl,a,b,c --flto' +test_var DLANG_LDFLAGS dmd-2.106 "-L-z -Lpack-relative-relocs -La -Lb -Lc --flto" +test_var DLANG_LDFLAGS gdc-13 "${LDFLAGS} -shared-libphobos" +test_var DLANG_LDFLAGS ldc2-1_36 "-L-z -Lpack-relative-relocs -La -Lb -Lc --flto" + +LDFLAGS='-Wl,-O1 -Xlinker --as-needed -garbage' +test_var DLANG_DMDW_LDFLAGS dmd-2.106 "-L-O1 -L--as-needed -garbage" +test_var DLANG_DMDW_LDFLAGS gdc-13 "-L-O1 -L--as-needed -garbage -q,-shared-libphobos" +test_var DLANG_DMDW_LDFLAGS ldc2-1_35 "-L-O1 -L--as-needed -garbage" + +test_var DLANG_LINKER_FLAG dmd-2.102 "-L" +test_var DLANG_LINKER_FLAG gdc-13 "-Wl," +test_var DLANG_LINKER_FLAG ldc2-1_36 "-L" + +test_var DLANG_OUTPUT_FLAG dmd-2.102 "-of" +test_var DLANG_OUTPUT_FLAG gdc-13 "-o" +test_var DLANG_OUTPUT_FLAG ldc2-1_36 "-of=" + +test_var DLANG_UNITTEST_FLAG dmd-2.102 "-unittest" +test_var DLANG_UNITTEST_FLAG gdc-13 "-funittest" +test_var DLANG_UNITTEST_FLAG ldc2-1_36 "-unittest" + +test_var DLANG_VERSION_FLAG dmd-2.102 "-version" +test_var DLANG_VERSION_FLAG gdc-13 "-fversion" +test_var DLANG_VERSION_FLAG ldc2-1_36 "-d-version" + +test_var DLANG_FE_VERSION dmd-2.102 2.102 +test_var DLANG_FE_VERSION gdc-13 2.103 +test_var DLANG_FE_VERSION ldc2-1_36 2.106 + +test_var DLANG_BE_VERSION dmd-2.102 2.102 +test_var DLANG_BE_VERSION gdc-13 13 +test_var DLANG_BE_VERSION ldc2-1_36 1.36 + +test_var DLANG_WNO_ERROR_FLAG dmd-2.102 -wi +test_var DLANG_WNO_ERROR_FLAG gdc-13 -Wno-error +test_var DLANG_WNO_ERROR_FLAG ldc2-1.36 --wi + +test_var DLANG_SYSTEM_IMPORT_PATHS dmd-2.101 "${EPREFIX}/usr/lib/dmd/2.101/import" +test_var DLANG_SYSTEM_IMPORT_PATHS gdc-13 "${EPREFIX}/usr/lib/gcc/${CHOST_default}/13/include/d" +test_var DLANG_SYSTEM_IMPORT_PATHS ldc2-1_32 "${EPREFIX}/usr/lib/ldc2/1.32/include/d" + +test_var DLANG_PKG_DEP dmd-2.102 "dev-lang/dmd:2.102=" +test_var DLANG_PKG_DEP gdc-12 "sys-devel/gcc:12[d] dev-util/gdmd:12" +test_var DLANG_PKG_DEP ldc2-1.36 "dev-lang/ldc2:1.36=" + +declare -A DLANG_REQ_USE=( + [dmd]="flag1" + [gdc]="flag2" + [ldc2]="flag3(-)?" +) +test_var DLANG_PKG_DEP dmd-2.102 "dev-lang/dmd:2.102=[flag1]" +test_var DLANG_PKG_DEP gdc-12 "sys-devel/gcc:12[d,flag2] dev-util/gdmd:12" +test_var DLANG_PKG_DEP ldc2-1.36 "dev-lang/ldc2:1.36=[flag3(-)?]" + +get_libdir() { + local libdir_var="LIBDIR_${ABI}" + echo "${!libdir_var}" +} +# multilib +MULTILIB_ABIS="amd64 x86" +DEFAULT_ABI=amd64 +LIBDIR_amd64=lib64 +LIBDIR_x86=lib + +ABI=amd64 +test_var DLANG_LIBDIR dmd-2.102 "lib/dmd/2.102/lib64" +test_var DLANG_LIBDIR gdc-12 "lib/gcc/${CHOST_default}/12" +test_var DLANG_LIBDIR ldc2-1.35 "lib/ldc2/1.35/lib64" +test_var DLANG_MODEL_FLAG ldc2-1.35 '-m64' +ABI=x86 +test_var DLANG_LIBDIR dmd-2.102 "lib/dmd/2.102/lib32" +test_var DLANG_LIBDIR gdc-12 "lib/gcc/${CHOST_default}/12/32" +test_var DLANG_LIBDIR ldc2-1.35 "lib/ldc2/1.35/lib32" +test_var DLANG_MODEL_FLAG ldc2-1.35 '-m32' + +# nomultilib +MULTILIB_ABIS=amd64 +DEFAULT_ABI=amd64 +LIBDIR_amd64=lib64 +ABI=amd64 +test_var DLANG_LIBDIR dmd-2.102 "lib/dmd/2.102/lib64" +test_var DLANG_LIBDIR gdc-12 "lib/gcc/${CHOST_default}/12" +test_var DLANG_LIBDIR ldc2-1.35 "lib/ldc2/1.35/lib64" +test_var DLANG_MODEL_FLAG ldc2-1.35 '' +LIBDIR_amd64=mylib +test_var DLANG_LIBDIR dmd-2.102 "lib/dmd/2.102/lib64" +test_var DLANG_LIBDIR gdc-12 "lib/gcc/${CHOST_default}/12" +test_var DLANG_LIBDIR ldc2-1.35 "lib/ldc2/1.35/mylib" + +MULTILIB_ABIS=x86 +DEFAULT_ABI=x86 +LIBDIR_x86=lib +ABI=x86 +test_var DLANG_LIBDIR dmd-2.102 "lib/dmd/2.102/lib" +test_var DLANG_LIBDIR gdc-12 "lib/gcc/${CHOST_default}/12" +test_var DLANG_LIBDIR ldc2-1.35 "lib/ldc2/1.35/lib" +test_var DLANG_MODEL_FLAG ldc2-1.35 '' +LIBDIR_x86=mylib +test_var DLANG_LIBDIR dmd-2.102 "lib/dmd/2.102/lib" +test_var DLANG_LIBDIR gdc-12 "lib/gcc/${CHOST_default}/12" +test_var DLANG_LIBDIR ldc2-1.35 "lib/ldc2/1.35/mylib" + +MULTILIB_ABIS=arm64 +DEFAULT_ABI=arm64 +LIBDIR_arm64=lib64 +ABI=arm64 +#test_var DLANG_LIBDIR dmd-2.102 "lib/dmd/2.102/lib64" +test_var DLANG_LIBDIR gdc-12 "lib/gcc/${CHOST_default}/12" +test_var DLANG_LIBDIR ldc2-1.35 "lib/ldc2/1.35/lib64" +test_var DLANG_MODEL_FLAG ldc2-1.35 '' + +assert_eq() { + local what=${1} expected=${2} + + [[ ${what} != ${expected} ]] && die "'${what}' != '${expected}'" +} + +# No $EDC set +assert_eq $(EDC= dlang_get_import_dir) "/usr/include/dlang" + +tbegin '_dlang_compile_extra_flags' + +imports="A B" +assert_eq "$(EDC=dmd-2.102 _dlang_compile_extra_flags)" "-IA -IB" +string_imports="c d" +assert_eq "$(EDC=dmd-2.102 _dlang_compile_extra_flags)" "-IA -IB -Jc -Jd" +versions="x yY" +assert_eq "$(EDC=dmd-2.102 _dlang_compile_extra_flags)" \ + "-IA -IB -Jc -Jd -version=x -version=yY" +libs="bar baz" +assert_eq "$(EDC=dmd-2.102 _dlang_compile_extra_flags)" \ + "-IA -IB -Jc -Jd -version=x -version=yY -L-lbar -L-lbaz" +assert_eq "$(EDC=gdc-12 _dlang_compile_extra_flags)" \ + "-IA -IB -Jc -Jd -fversion=x -fversion=yY -Wl,-lbar -Wl,-lbaz" +assert_eq "$(EDC=ldc2-1.35 _dlang_compile_extra_flags)" \ + "-I=A -I=B -J=c -J=d -d-version=x -d-version=yY -L-lbar -L-lbaz" +tend + + +tbegin "that _dlang_verify_patterns accepts frontend versions" +( _dlang_verify_patterns "2.100" "2.107" ) +tend ${?} + +tbegin "_dlang_impl_matches on frontend version" +_dlang_impl_matches "gdc-13" "2.103" +tend ${?} + +# check _dlang_impl_matches behavior +einfo "Testing dlang_impl_matches" +eindent +test_is "_dlang_impl_matches gdc-13 2.103" 0 +test_is "_dlang_impl_matches dmd-2_107 2.107" 0 +test_is "_dlang_impl_matches ldc2-1_36 2.106" 0 +set -f +test_is "_dlang_impl_matches gdc-13 gdc*" 0 +test_is "_dlang_impl_matches dmd-2.103 gdc*" 1 +test_is "_dlang_impl_matches gdc-13 dmd-2_103" 1 +test_is "_dlang_impl_matches dmd-2.107 dmd-2.0*" 1 +test_is "_dlang_impl_matches ldc2-1_34 ldc2*" 0 +set +f +test_is "_dlang_impl_matches gdc-12 2.100" 0 +test_is "_dlang_impl_matches gdc-12 2.086" 1 +test_is "_dlang_impl_matches gdc-12 2.103" 1 +test_is "_dlang_impl_matches dmd-2_107 2.107" 0 +test_is "_dlang_impl_matches dmd-2_107 2.106" 1 +test_is "_dlang_impl_matches dmd-2_107 2.103" 1 +test_is "_dlang_impl_matches ldc2-1_36 2.107" 1 +test_is "_dlang_impl_matches ldc2-1_36 2.103" 1 + +# Check for the oldest frontend version patterns +test_is "_dlang_impl_matches gdc-12 2.100" 0 +test_is "_dlang_impl_matches dmd-2.101 2.101" 0 +test_is "_dlang_impl_matches ldc2-1.32 2.102" 0 +eoutdent + +tbegin "simple dlang-filter-dflags" +EDC=dmd-2.105 +DMDFLAGS='-O --color -mcpu=native' +dlang-filter-dflags "dmd*" "--col*" +[[ "${DMDFLAGS}" == "-O -mcpu=native" ]] +tend $? + +tbegin "propagation of flag changes done by dlang-filter-dflags" +EDC=gdc-12 +GDCFLAGS='-march=native -O2 -pipe' +_dlang_export "${EDC}" DCFLAGS DMDW_DCFLAGS +dlang-filter-dflags "gdc*" "-march=native" +[[ "${GDCFLAGS}" == "-O2 -pipe" ]] && + [[ "${DCFLAGS}" == "-O2 -pipe" ]] && + [[ "${DMDW_DCFLAGS}" == "-q,-O2 -q,-pipe" ]] +tend $? |