During migration of dev-lua/* ebuilds to slotted lua, I noticed, that
many ebuilds use 'dev-lua/busted' for running tests. This change adds
support for running a test-runner, at first only 'busted' for now.
Also a non-color and plaintext output will be used for the test-runner 'busted'.

This is basically a copy of the test-runner section, written by mgorny,
which already exists in 'distutils-r1', but modified and adapted to lua.

In order to use this feature, you can define 'lua_enable_tests busted'
to setup everything needed for tests and run them. By default,
'dev-lua/busted' assumes, that tests are in the 'spec' folder.

If this is not the case, you can add a second argument to specify a
different folder. For example, if the folder is called 'foo', you can
just run 'lua_enable_tests busted foo'.

More test-runners can be added in future, if needed.

PATCH v2 has two changes:
- removed EAPI condition, as lua-utils is EAPI=7 only.
- make test_directoy as a local variable and use eval in src_test to
  read it.

Signed-off-by: Conrad Kostecki <conik...@gentoo.org>
---
 eclass/lua-utils.eclass |  69 ++++++
 lua-utils.eclass        | 532 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 601 insertions(+)
 create mode 100644 lua-utils.eclass

diff --git a/eclass/lua-utils.eclass b/eclass/lua-utils.eclass
index 100be14cb08..2200aa50391 100644
--- a/eclass/lua-utils.eclass
+++ b/eclass/lua-utils.eclass
@@ -344,6 +344,75 @@ _lua_export() {
        done
 }
 
+# @FUNCTION: lua_enable_tests
+# @USAGE: <test-runner> <test-directory>
+# @DESCRIPTION:
+# Set up IUSE, RESTRICT, BDEPEND and src_test() for running tests
+# with the specified test runner.  Also copies the current value
+# of RDEPEND to test?-BDEPEND.  The test-runner argument must be one of:
+#
+# - busted: dev-lua/busted
+#
+# Additionally, a second argument can be passed after <test-runner>,
+# so <test-runner> will use that directory to search for tests.
+# If not passed, a default directory of <test-runner> will be used.
+#
+# - busted: spec
+#
+# This function is meant as a helper for common use cases, and it only
+# takes care of basic setup.  You still need to list additional test
+# dependencies manually.  If you have uncommon use case, you should
+# not use it and instead enable tests manually.
+#
+# This function must be called in global scope, after RDEPEND has been
+# declared.  Take care not to overwrite the variables set by it.
+lua_enable_tests() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       [[ ${#} -ge 1 ]] || die "${FUNCNAME} takes at least one argument: 
test-runner (test-directory)"
+       local test_pkg
+       case ${1} in
+               busted)
+                       test_directory="${2:-spec}"
+                       test_pkg="dev-lua/busted"
+                       if [[ ! ${_LUA_SINGLE_R0} ]]; then
+                               lua_src_test() {
+                                       busted --lua="${ELUA}" 
--output="plainTerminal" "${test_directory}" || die "Tests fail with ${ELUA}"
+                               }
+                               src_test() {
+                                       lua_foreach_impl lua_src_test
+                               }
+                       else
+                               src_test() {
+                                       busted --lua="${ELUA}" 
--output="plainTerminal" "${test_directory}" || die "Tests fail with ${ELUA}"
+                               }
+                       fi
+                       ;;
+               *)
+                       die "${FUNCNAME}: unsupported argument: ${1}"
+       esac
+
+       local test_deps=${RDEPEND}
+       if [[ -n ${test_pkg} ]]; then
+               if [[ ! ${_LUA_SINGLE_R0} ]]; then
+                       test_deps+=" ${test_pkg}[${LUA_USEDEP}]"
+               else
+                       test_deps+=" $(lua_gen_cond_dep "
+                               ${test_pkg}[\${LUA_USEDEP}]
+                       ")"
+               fi
+       fi
+       if [[ -n ${test_deps} ]]; then
+               IUSE+=" test"
+               RESTRICT+=" !test? ( test )"
+               BDEPEND+=" test? ( ${test_deps} )"
+       fi
+
+       # we need to ensure successful return in case we're called last,
+       # otherwise Portage may wrongly assume sourcing failed
+       return 0
+}
+
 # @FUNCTION: lua_get_CFLAGS
 # @USAGE: [<impl>]
 # @DESCRIPTION:
diff --git a/lua-utils.eclass b/lua-utils.eclass
new file mode 100644
index 00000000000..0589318ef51
--- /dev/null
+++ b/lua-utils.eclass
@@ -0,0 +1,532 @@
+# Copyright 1999-2020 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+# @ECLASS: lua-utils.eclass
+# @MAINTAINER:
+# William Hubbs <willi...@gentoo.org>
+# Marek Szuba <mare...@gentoo.org>
+# @AUTHOR:
+# Marek Szuba <mare...@gentoo.org>
+# Based on python-utils-r1.eclass by Michał Górny <mgo...@gentoo.org> et al.
+# @SUPPORTED_EAPIS: 7
+# @BLURB: Utility functions for packages with Lua parts
+# @DESCRIPTION:
+# A utility eclass providing functions to query Lua implementations,
+# install Lua modules and scripts.
+#
+# This eclass neither sets any metadata variables nor exports any phase
+# functions. It can be inherited safely.
+
+case ${EAPI:-0} in
+       0|1|2|3|4|5|6)
+               die "Unsupported EAPI=${EAPI} (too old) for ${ECLASS}"
+               ;;
+       7)
+               ;;
+       *)
+               die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}"
+               ;;
+esac
+
+if [[ ! ${_LUA_UTILS_R0} ]]; then
+
+inherit toolchain-funcs
+
+# @ECLASS-VARIABLE: _LUA_ALL_IMPLS
+# @INTERNAL
+# @DESCRIPTION:
+# All supported Lua implementations, most preferred last
+_LUA_ALL_IMPLS=(
+       luajit
+       lua5-1
+       lua5-2
+       lua5-3
+       lua5-4
+)
+readonly _LUA_ALL_IMPLS
+
+# @FUNCTION: _lua_set_impls
+# @INTERNAL
+# @DESCRIPTION:
+# Check LUA_COMPAT for well-formedness and validity, then set
+# two global variables:
+#
+# - _LUA_SUPPORTED_IMPLS containing valid implementations supported
+#   by the ebuild (LUA_COMPAT minus dead implementations),
+#
+# - and _LUA_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 only be called once.
+_lua_set_impls() {
+       local i
+
+       if ! declare -p LUA_COMPAT &>/dev/null; then
+               die 'LUA_COMPAT not declared.'
+       fi
+       if [[ $(declare -p LUA_COMPAT) != "declare -a"* ]]; then
+               die 'LUA_COMPAT must be an array.'
+       fi
+
+       local supp=() unsupp=()
+
+       for i in "${_LUA_ALL_IMPLS[@]}"; do
+               if has "${i}" "${LUA_COMPAT[@]}"; then
+                       supp+=( "${i}" )
+               else
+                       unsupp+=( "${i}" )
+               fi
+       done
+
+       if [[ ! ${supp[@]} ]]; then
+               die "No supported implementation in LUA_COMPAT."
+       fi
+
+       if [[ ${_LUA_SUPPORTED_IMPLS[@]} ]]; then
+               # set once already, verify integrity
+               if [[ ${_LUA_SUPPORTED_IMPLS[@]} != ${supp[@]} ]]; then
+                       eerror "Supported impls (LUA_COMPAT) changed between 
inherits!"
+                       eerror "Before: ${_LUA_SUPPORTED_IMPLS[*]}"
+                       eerror "Now   : ${supp[*]}"
+                       die "_LUA_SUPPORTED_IMPLS integrity check failed"
+               fi
+               if [[ ${_LUA_UNSUPPORTED_IMPLS[@]} != ${unsupp[@]} ]]; then
+                       eerror "Unsupported impls changed between inherits!"
+                       eerror "Before: ${_LUA_UNSUPPORTED_IMPLS[*]}"
+                       eerror "Now   : ${unsupp[*]}"
+                       die "_LUA_UNSUPPORTED_IMPLS integrity check failed"
+               fi
+       else
+               _LUA_SUPPORTED_IMPLS=( "${supp[@]}" )
+               _LUA_UNSUPPORTED_IMPLS=( "${unsupp[@]}" )
+               readonly _LUA_SUPPORTED_IMPLS _LUA_UNSUPPORTED_IMPLS
+       fi
+}
+
+# @FUNCTION: _lua_wrapper_setup
+# @USAGE: [<path> [<impl>]]
+# @INTERNAL
+# @DESCRIPTION:
+# Create proper Lua executables and pkg-config wrappers
+# (if available) in the directory named by <path>. Set up PATH
+# and PKG_CONFIG_PATH appropriately. <path> defaults to ${T}/${ELUA}.
+#
+# The wrappers will be created for implementation named by <impl>,
+# or for one named by ${ELUA} if no <impl> passed.
+#
+# If the named directory contains a lua symlink already, 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.
+_lua_wrapper_setup() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local workdir=${1:-${T}/${ELUA}}
+       local impl=${2:-${ELUA}}
+
+       [[ ${workdir} ]] || die "${FUNCNAME}: no workdir specified."
+       [[ ${impl} ]] || die "${FUNCNAME}: no impl nor ELUA specified."
+
+       if [[ ! -x ${workdir}/bin/lua ]]; then
+               mkdir -p "${workdir}"/{bin,pkgconfig} || die
+
+               # Clean up, in case we were supposed to do a cheap update
+               rm -f "${workdir}"/bin/lua{,c} || die
+               rm -f "${workdir}"/pkgconfig/lua.pc || die
+
+               local ELUA LUA
+               _lua_export "${impl}" ELUA LUA
+
+               # Lua interpreter
+               ln -s "${EPREFIX}"/usr/bin/${ELUA} "${workdir}"/bin/lua || die
+
+               # Lua compiler, or a stub for it in case of luajit
+               if [[ ${ELUA} == luajit ]]; then
+                       # Just in case
+                       ln -s "${EPREFIX}"/bin/true "${workdir}"/bin/luac || die
+               else
+                       ln -s "${EPREFIX}"/usr/bin/${ELUA/a/ac} 
"${workdir}"/bin/luac || die
+               fi
+
+               # pkg-config
+               ln -s "${EPREFIX}"/usr/$(get_libdir)/pkgconfig/${ELUA}.pc \
+                       "${workdir}"/pkgconfig/lua.pc || die
+       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
+}
+
+# @ECLASS-VARIABLE: ELUA
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# The executable name of the current Lua interpreter. This variable is set
+# automatically in functions called by lua_foreach_impl().
+#
+# Example value:
+# @CODE
+# lua5.1
+# @CODE
+
+# @ECLASS-VARIABLE: LUA
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# The absolute path to the current Lua interpreter. This variable is set
+# automatically in functions called by lua_foreach_impl().
+#
+# Example value:
+# @CODE
+# /usr/bin/lua5.1
+# @CODE
+
+# @FUNCTION: _lua_get_library_file
+# @USAGE: <impl>
+# @INTERNAL
+# @DESCRIPTION:
+# Get the core part (i.e. without the extension) of the library name,
+# with path, of the given Lua implementation.
+# Used internally by _lua_export().
+_lua_get_library_file() {
+       local impl="${1}"
+       local libdir libname
+
+       case ${impl} in
+               luajit)
+                       libname=lib$($(tc-getPKG_CONFIG) --variable libname 
${impl}) || die
+                       ;;
+               lua*)
+                       libname=lib${impl}
+                       ;;
+               *)
+                       die "Invalid implementation: ${impl}"
+                       ;;
+       esac
+       libdir=$($(tc-getPKG_CONFIG) --variable libdir ${impl}) || die
+
+       debug-print "${FUNCNAME}: libdir = ${libdir}, libname = ${libname}"
+       echo "${libdir}/${libname}"
+}
+
+# @FUNCTION: _lua_export
+# @USAGE: [<impl>] <variables>...
+# @INTERNAL
+# @DESCRIPTION:
+# Set and export the Lua implementation-relevant variables passed
+# as parameters.
+#
+# The optional first parameter may specify the requested Lua
+# implementation (either as LUA_TARGETS value, e.g. lua5-2,
+# or an ELUA one, e.g. lua5.2). If no implementation passed,
+# the current one will be obtained from ${ELUA}.
+_lua_export() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local impl var
+
+       case "${1}" in
+               luajit)
+                       impl=${1}
+                       shift
+                       ;;
+               lua*)
+                       impl=${1/-/.}
+                       shift
+                       ;;
+               *)
+                       impl=${ELUA}
+                       if [[ -z ${impl} ]]; then
+                               die "_lua_export called without a Lua 
implementation and ELUA is unset"
+                       fi
+                       ;;
+       esac
+       debug-print "${FUNCNAME}: implementation: ${impl}"
+
+       for var; do
+               case "${var}" in
+                       ELUA)
+                               export ELUA=${impl}
+                               debug-print "${FUNCNAME}: ELUA = ${ELUA}"
+                               ;;
+                       LUA)
+                               export LUA="${EPREFIX}"/usr/bin/${impl}
+                               debug-print "${FUNCNAME}: LUA = ${LUA}"
+                               ;;
+                       LUA_CFLAGS)
+                               local val
+
+                               val=$($(tc-getPKG_CONFIG) --cflags ${impl}) || 
die
+
+                               export LUA_CFLAGS=${val}
+                               debug-print "${FUNCNAME}: LUA_CFLAGS = 
${LUA_CFLAGS}"
+                               ;;
+                       LUA_CMOD_DIR)
+                               local val
+
+                               val=$($(tc-getPKG_CONFIG) --variable 
INSTALL_CMOD ${impl}) || die
+
+                               export LUA_CMOD_DIR=${val}
+                               debug-print "${FUNCNAME}: LUA_CMOD_DIR = 
${LUA_CMOD_DIR}"
+                               ;;
+                       LUA_INCLUDE_DIR)
+                               local val
+
+                               val=$($(tc-getPKG_CONFIG) --variable includedir 
${impl}) || die
+
+                               export LUA_INCLUDE_DIR=${val}
+                               debug-print "${FUNCNAME}: LUA_INCLUDE_DIR = 
${LUA_INCLUDE_DIR}"
+                               ;;
+                       LUA_LIBS)
+                               local val
+
+                               val=$($(tc-getPKG_CONFIG) --libs ${impl}) || die
+
+                               export LUA_LIBS=${val}
+                               debug-print "${FUNCNAME}: LUA_LIBS = 
${LUA_LIBS}"
+                               ;;
+                       LUA_LMOD_DIR)
+                               local val
+
+                               val=$($(tc-getPKG_CONFIG) --variable 
INSTALL_LMOD ${impl}) || die
+
+                               export LUA_LMOD_DIR=${val}
+                               debug-print "${FUNCNAME}: LUA_LMOD_DIR = 
${LUA_LMOD_DIR}"
+                               ;;
+                       LUA_PKG_DEP)
+                               local d
+                               case ${impl} in
+                                       luajit)
+                                               LUA_PKG_DEP="dev-lang/luajit:="
+                                               ;;
+                                       lua*)
+                                               
LUA_PKG_DEP="dev-lang/lua:${impl#lua}"
+                                               ;;
+                                       *)
+                                               die "Invalid implementation: 
${impl}"
+                                               ;;
+                               esac
+
+                               # use-dep
+                               if [[ ${LUA_REQ_USE} ]]; then
+                                       LUA_PKG_DEP+=[${LUA_REQ_USE}]
+                               fi
+
+                               export LUA_PKG_DEP
+                               debug-print "${FUNCNAME}: LUA_PKG_DEP = 
${LUA_PKG_DEP}"
+                               ;;
+                       LUA_SHARED_LIB)
+                               local val=$(_lua_get_library_file ${impl})
+                               export LUA_SHARED_LIB="${val}".so
+                               debug-print "${FUNCNAME}: LUA_SHARED_LIB = 
${LUA_SHARED_LIB}"
+                               ;;
+                       LUA_VERSION)
+                               local val
+
+                               val=$($(tc-getPKG_CONFIG) --modversion ${impl}) 
|| die
+
+                               export LUA_VERSION=${val}
+                               debug-print "${FUNCNAME}: LUA_VERSION = 
${LUA_VERSION}"
+                               ;;
+                       *)
+                               die "_lua_export: unknown variable ${var}"
+                               ;;
+               esac
+       done
+}
+
+# @FUNCTION: lua_enable_tests
+# @USAGE: <test-runner> <test-directory>
+# @DESCRIPTION:
+# Set up IUSE, RESTRICT, BDEPEND and src_test() for running tests
+# with the specified test runner.  Also copies the current value
+# of RDEPEND to test?-BDEPEND.  The test-runner argument must be one of:
+#
+# - busted: dev-lua/busted
+#
+# Additionally, a second argument can be passed after <test-runner>,
+# so <test-runner> will use that directory to search for tests.
+# If not passed, a default directory of <test-runner> will be used.
+#
+# - busted: spec
+#
+# This function is meant as a helper for common use cases, and it only
+# takes care of basic setup.  You still need to list additional test
+# dependencies manually.  If you have uncommon use case, you should
+# not use it and instead enable tests manually.
+#
+# This function must be called in global scope, after RDEPEND has been
+# declared.  Take care not to overwrite the variables set by it.
+lua_enable_tests() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       [[ ${#} -ge 1 ]] || die "${FUNCNAME} takes at least one argument: 
test-runner (test-directory)"
+       local test_directory
+       local test_pkg
+       case ${1} in
+               busted)
+                       test_directory="${2:-spec}"
+                       test_pkg="dev-lua/busted"
+                       if [[ ! ${_LUA_SINGLE_R0} ]]; then
+                               eval "lua_src_test() {
+                                       busted --lua=\"\${ELUA}\" 
--output=\"plainTerminal\" \"${test_directory}\" || die \"Tests fail with 
\${ELUA}\"
+                               }"
+                               src_test() {
+                                       lua_foreach_impl lua_src_test
+                               }
+                       else
+                               eval "src_test() {
+                                       busted --lua=\"\${ELUA}\" 
--output=\"plainTerminal\" \"${test_directory}\" || die \"Tests fail with 
\${ELUA}\"
+                               }"
+                       fi
+                       ;;
+               *)
+                       die "${FUNCNAME}: unsupported argument: ${1}"
+       esac
+
+       local test_deps=${RDEPEND}
+       if [[ -n ${test_pkg} ]]; then
+               if [[ ! ${_LUA_SINGLE_R0} ]]; then
+                       test_deps+=" ${test_pkg}[${LUA_USEDEP}]"
+               else
+                       test_deps+=" $(lua_gen_cond_dep "
+                               ${test_pkg}[\${LUA_USEDEP}]
+                       ")"
+               fi
+       fi
+       if [[ -n ${test_deps} ]]; then
+               IUSE+=" test"
+               RESTRICT+=" !test? ( test )"
+               BDEPEND+=" test? ( ${test_deps} )"
+       fi
+
+       # we need to ensure successful return in case we're called last,
+       # otherwise Portage may wrongly assume sourcing failed
+       return 0
+}
+
+# @FUNCTION: lua_get_CFLAGS
+# @USAGE: [<impl>]
+# @DESCRIPTION:
+# Obtain and print the compiler flags for building against Lua,
+# for the given implementation. If no implementation is provided,
+# ${ELUA} will be used.
+#
+# Please note that this function requires Lua and pkg-config installed,
+# and therefore proper build-time dependencies need be added to the ebuild.
+lua_get_CFLAGS() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       _lua_export "${@}" LUA_CFLAGS
+       echo "${LUA_CFLAGS}"
+}
+
+# @FUNCTION: lua_get_cmod_dir
+# @USAGE: [<impl>]
+# @DESCRIPTION:
+# Obtain and print the name of the directory into which compiled Lua
+# modules are installed, for the given implementation. If no implementation
+# is provided, ${ELUA} will be used.
+#
+# Please note that this function requires Lua and pkg-config installed,
+# and therefore proper build-time dependencies need be added to the ebuild.
+lua_get_cmod_dir() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       _lua_export "${@}" LUA_CMOD_DIR
+       echo "${LUA_CMOD_DIR}"
+}
+
+# @FUNCTION: lua_get_include_dir
+# @USAGE: [<impl>]
+# @DESCRIPTION:
+# Obtain and print the name of the directory containing header files
+# of the given Lua implementation. If no implementation is provided,
+# ${ELUA} will be used.
+#
+# Please note that this function requires Lua and pkg-config installed,
+# and therefore proper build-time dependencies need be added to the ebuild.
+lua_get_include_dir() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       _lua_export "${@}" LUA_INCLUDE_DIR
+       echo "${LUA_INCLUDE_DIR}"
+}
+
+# @FUNCTION: lua_get_LIBS
+# @USAGE: [<impl>]
+# @DESCRIPTION:
+# Obtain and print the compiler flags for linking against Lua,
+# for the given implementation. If no implementation is provided,
+# ${ELUA} will be used.
+#
+# Please note that this function requires Lua and pkg-config installed,
+# and therefore proper build-time dependencies need be added to the ebuild.
+lua_get_LIBS() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       _lua_export "${@}" LUA_LIBS
+       echo "${LUA_LIBS}"
+}
+
+# @FUNCTION: lua_get_lmod_dir
+# @USAGE: [<impl>]
+# @DESCRIPTION:
+# Obtain and print the name of the directory into which native-Lua
+# modules are installed, for the given implementation. If no implementation
+# is provided, ${ELUA} will be used.
+#
+# Please note that this function requires Lua and pkg-config installed,
+# and therefore proper build-time dependencies need be added to the ebuild.
+lua_get_lmod_dir() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       _lua_export "${@}" LUA_LMOD_DIR
+       echo "${LUA_LMOD_DIR}"
+}
+
+# @FUNCTION: lua_get_shared_lib
+# @USAGE: [<impl>]
+# @DESCRIPTION:
+# Obtain and print the expected name, with path, of the main shared library
+# of the given Lua implementation. If no implementation is provided,
+# ${ELUA} will be used.
+#
+# Note that it is up to the ebuild maintainer to ensure Lua actually
+# provides a shared library.
+#
+# Please note that this function requires Lua and pkg-config installed,
+# and therefore proper build-time dependencies need be added to the ebuild.
+lua_get_shared_lib() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       _lua_export "${@}" LUA_SHARED_LIB
+       echo "${LUA_SHARED_LIB}"
+}
+
+# @FUNCTION: lua_get_version
+# @USAGE: [<impl>]
+# @DESCRIPTION:
+# Obtain and print the full version number of the given Lua implementation.
+# If no implementation is provided, ${ELUA} will be used.
+#
+# Please note that this function requires Lua and pkg-config installed,
+# and therefore proper build-time dependencies need be added to the ebuild.
+lua_get_version() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       _lua_export "${@}" LUA_VERSION
+       echo "${LUA_VERSION}"
+}
+
+_LUA_UTILS_R0=1
+fi
-- 
2.30.0


Reply via email to