# Copyright 2024 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 # @ECLASS: gap-pkg.eclass # @MAINTAINER: # François Bissey # Michael Orlitzky # Gentoo Mathematics Project # @AUTHOR: # François Bissey # Michael Orlitzky # @SUPPORTED_EAPIS: 8 # @BLURB: Simplify the installation of GAP packages. # @DESCRIPTION: # The main purpose of this eclass is to build and install GAP packages # that typically occupy the dev-gap category. Most GAP packages do not # support an install target out of the box, so the default installation # is "by hand," with attention paid to those directories that are part # of the recommended layout. The prepare, configure, and compile phases # do however try to support packages having a real build system. # # GAP itself has four "required" packages that are packaged separately, # making dependencies between them somewhat weird. The four required # packages are, # # * dev-gap/gapdoc # * dev-gap/primgrp # * dev-gap/smallgrp # * dev-gap/transgrp # # Those four packages will have only sci-mathematics/gap added to # RDEPEND. All other packages will have the four required packages above # added to RDEPEND in addition to sci-mathematics/gap. In theory it # would be better to list all dependencies explicitly rather than # grouping together the "required" four, but this is how upstream GAP # works, and is what all GAP packages expect; for example, most test # suites fail without the required packages but make no attempt to load # them. # # If you need a version constraint on sci-mathematics/gap, you'll have # to specify it yourself. Compiled packages will likely need # sci-mathematics/gap in DEPEND as well, and may also want a subslot # dependency. if [[ -z ${_GAP_PKG_ECLASS} ]]; then _GAP_PKG_ECLASS=1 case ${EAPI} in 8) ;; *) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;; esac # For eshopts_push and eshopts_pop inherit estack # Some packages have additional homepages, but pretty much every GAP # package can be found at this URL. HOMEPAGE="https://www.gap-system.org/packages/#${PN}" # _GAP_PKG_IS_REQUIRED is an internal variable that indicates whether or # not $PN is one of the four "required" GAP packages that are always # loaded, even when GAP is started with the "-A" flag. We treat this # four somewhat differently since they are implicit dependencies of # everything else in the GAP ecosystem. _GAP_PKG_IS_REQUIRED=no case ${CATEGORY}/${PN} in dev-gap/gapdoc|dev-gap/smallgrp|dev-gap/primgrp|dev-gap/transgrp) _GAP_PKG_IS_REQUIRED=yes ;; *) ;; esac # _GAP_PKG_RDEPEND is an internal variable to hold the RDEPEND entries # added by this eclass. We use a separate variable for this because we # need its contents later in gap-pkg_enable_tests, and that function is # called from an ebuild context where the list of RDEPEND is maintained # separately. Basically: the values we add to RDEPEND here do not appear # in RDEPEND when gap-pkg_enable_tests is called. _GAP_PKG_RDEPEND="sci-mathematics/gap" # The four "required" packages depend only on GAP itself, while every # other package depends (also) on the four required ones. if [[ "${_GAP_PKG_IS_REQUIRED}" = "no" ]]; then _GAP_PKG_RDEPEND+=" dev-gap/gapdoc dev-gap/smallgrp dev-gap/primgrp dev-gap/transgrp" fi RDEPEND="${_GAP_PKG_RDEPEND}" # @FUNCTION: gap-pkg_dir # @DESCRIPTION: # The directory into which the gap package should be installed. The # accepted current location is /usr/$(get_libdir)/gap/pkg, but # technically this depends on the econf call in sci-mathematics/gap. gap-pkg_dir() { echo "/usr/$(get_libdir)/gap/pkg/${PN}" } # @FUNCTION: _gap-pkg_gaproot # @INTERNAL # @DESCRIPTION: # The directory containing sysinfo.gap. This is frequently passed to GAP # packages via ./configure --with-gaproot or as a positional argument to # hand-written configure scripts. We also use it to find the value of # $GAParch, which is contained in sysinfo.gap. The "gaproot" is # implicitly determined by the econf call in sci-mathematics/gap. As a # result, calling this function requires sci-mathematics/gap at # build-time. _gap-pkg_gaproot() { echo "${ESYSROOT}/usr/$(get_libdir)/gap" } # @FUNCTION: gap-pkg_econf # @USAGE: [extra econf args] # @DESCRIPTION: # Call econf, passing the value of _gap-pkg_gaproot to --with-gaproot. # All arguments to gap-pkg_econf are passed through to econf. # # @EXAMPLE # src_configure() { # gap-pkg_econf --with-external-libsemigroups # } # gap-pkg_econf() { econf --with-gaproot="$(_gap-pkg_gaproot)" "${@}" } # @FUNCTION: gap-pkg_src_configure # @DESCRIPTION: # Handle both autoconf configure scripts and the hand-written ones used # by many GAP packages. We determine which one we're dealing with by # running ./configure --help; an autoconf configure script will mention # "PREFIX" in the output, the others will not. # # Autoconf configure scripts are configured using gap-pkg_econf, while # hand-written ones are executed directly with _gap-pkg_gaproot as their # sole positional argument. gap-pkg_src_configure() { local _configure="${ECONF_SOURCE:-.}/configure" if [[ -x ${_configure} ]] ; then if ${_configure} --help | grep PREFIX &>/dev/null; then # This is an autoconf ./configure script gap-pkg_econf else # It's an "old-style" handwritten script that does # not print usage information with --help. ${_configure} $(_gap-pkg_gaproot) || die fi fi } # @FUNCTION: gap-pkg_src_compile # @DESCRIPTION: # The default src_compile with the addition of V=1 to emake. The # Makefile.gappkg used to build most C packages defaults to a quiet # build without this. gap-pkg_src_compile() { if [[ -f Makefile ]] || [[ -f GNUmakefile ]] || [[ -f makefile ]]; then emake V=1 || die "emake failed" fi } # @FUNCTION: gap-pkg_enable_tests # @DESCRIPTION: # Amend IUSE, RESTRICT, and BDEPEND for a package with a test suite. # This is modeled on similar functions in the distutils-r1 and # elisp-common eclasses, except here only a single default testing # strategy is supported. All runtime and post-merge dependencies are # added as build dependencies if USE=test is set. gap-pkg_enable_tests() { IUSE+=" test " RESTRICT+=" !test? ( test ) " # Use the internal variable here, too, because the RDEPEND list from # the ebuild is maintained separately by the package manager. We add # PDEPEND too because we use it to break some circular dependencies # between e.g. polycyclic and alnuth. BDEPEND+=" test? ( ${_GAP_PKG_RDEPEND} ${RDEPEND} ${PDEPEND} ) " } # @FUNCTION: gap-pkg_src_test # @DESCRIPTION: # Run this package's test suite if it has one. The GAP TestPackage # function is the standard way to do this, but it does rely on the # package itself to get a few things right, like running the tests # verbosely and exiting with the appropriate code. The alternative would # be run TestDirectory ourselves on "tst", but that has its own issues; # in particular many packages have set-up code that is run only with # TestPackage. YMMV. gap-pkg_src_test() { [[ -f PackageInfo.g ]] || return # We would prefer --bare to -A so that we can test (say) primgrp # after installing only gapdoc and not smallgrp or transgrp. But, # that would cause problems for basically every non-required # package, because they usually don't explicitly load the four # "required" packages in their test suites. So we use -A unless # this is one of the chosen four. local bareflag="--bare" if [[ "${_GAP_PKG_IS_REQUIRED}" = "no" ]]; then bareflag="-A" fi # Run GAP non-interactively to test the just-built package. We omit # the "-r" flag here because we use the UserGapRoot directory to # store AtlasRep data, and without it, the atlasrep tests (and the # tests of any packages depending on it) will fail. local gapcmd="gap -R ${bareflag} --nointeract" # ForceQuitGap translates a boolean return value to the expected # zero or one, useful for packages that set a single *.tst file as # their TestFile. gapcmd+=" -c ForceQuitGap(TestPackage(\"${PN}\"));" # Fake the directory structure that GAP needs to be able to find # packages with a symlink under ${T}, then prepend ${T} to the list # of search paths so that if this package is already installed, we # load the just-built copy first. ln -s "${WORKDIR}" "${T}/pkg" || die gapcmd+=" --roots ${T}/; " # False negatives can occur if GAP fails to start, or if there are # syntax errors: # # https://github.com/gap-system/gap/issues/5541 # # There's nothing you can do about that, but now you know. # # The pipe to tee is more important than it looks. Any test suite # involving dev-gap/browse is likely to bork the user's terminal. # The "browse" package is however smart enough to figure out when # stdout is not a tty, and avoids breaking it in that case. So by # piping to tee, we encourage it not to do anything too crazy. eshopts_push -o pipefail ${gapcmd} | tee test-suite.log \ || die "test suite failed, see test-suite.log" eshopts_pop } # @ECLASS_VARIABLE: GAP_PKG_EXTRA_INSTALL # @DEFAULT_UNSET # @DESCRIPTION: # A bash array of extra files and directories to install recursively at # the root of this package's directory tree. For example, if you have a # package that mostly follows the suggested layout (described in the # gap-pkg_src_install documentation) but also includes a "data" # directory, you should set # # GAP_PKG_EXTRA_INSTALL=( data ) # # to install the data directory without having to override the entire # src_install phase. # @ECLASS_VARIABLE: GAP_PKG_HTML_DOCDIR # @DESCRIPTION: # The directory inside the tarball where the HTML documentation is # located. This is _usually_ "doc", which conforms to the suggested # GAPDoc layout and is the default value of this variable. Many # packages however use a top-level "htm" directory instead. The named # directory will be installed to gap-pkg_dir and symlinked to the usual # location under /usr/share/doc. As a result, you should only use this # for directories referenced by PackageInfo.g or by some other part of # the package. HTML documentation whose location doesn't need to be # known to the package at runtime should instead be installed with # HTML_DOCS or a similar mechanism. : "${GAP_PKG_HTML_DOCDIR:=doc}" # @FUNCTION: gap-pkg_src_install # @DESCRIPTION: # Install a GAP package that follows the suggested layout, # # https://docs.gap-system.org/doc/ref/chap76.html # # In particular: # # 1. All GAP source files (*.g) in $S are installed. # # 2. If a library directory named "gap" or "lib" exists, # it is installed. # # 3. If a binary directory "bin" exists, it is installed. # # 4. If a "doc" directory exists, we assume GAPDoc conventions # (https://docs.gap-system.org/pkg/gapdoc/doc/chap5.html) and install # what we find there. Unfortunately for us, each package's # PackageInfo.g contains a "PackageDoc" section that points to this # documentation, and we can't break the paths it references. Instead, # we try to dosym the human-readable parts of the documentation (PDF # manuals) into appropriate Gentoo locations. # # 5. We consult GAP_PKG_HTML_DOCDIR for the HTML documentation and repeat # the process above. # # A few GAP packages have autotools build systems with working "make # install" routines, but most don't. So for the time being we omit that # step. It's harder to work around the packages that don't support it # than the other way around. gap-pkg_src_install() { einstalldocs # So we don't have to "test -f" on the result of every glob. eshopts_push -s nullglob # Install the "normal" documentation from the doc directory. This # includes anything the interactive GAP help might need in addition # to the documentation intended for direct user consumption. if [[ -d doc ]]; then pushd doc > /dev/null || die local docdir="$(gap-pkg_dir)/doc" insinto "${docdir}" # These files are needed by the GAP interface. We don't symlink # these because they're not meant for direct human consumption; # the text files are not *plain* text -- they contain color # codes. I'm not sure if the BibTeX files are actually used, # but the GAP packaging documentation mentions specifically # that they should be included. XML files are included in case # the bibliography is in BibXMLext format, but you may wind up # with some additional GAPDoc (XML) source files as a result. for f in *.{bib,lab,six,tex,txt,xml}; do doins "${f}" done # The PDF docs are also potentially used by the interface, since # they appear in PackageInfo.g, so we install them "as is." But # then afterwards we symlink them to their proper Gentoo # locations for f in *.pdf; do doins "${f}" dosym -r "${docdir}/${f}" "/usr/share/doc/${PF}/${f}" done popd > /dev/null || die fi # Install the HTML documentation. The procedure is basically the # same as for the PDF docs. if [[ -d "${GAP_PKG_HTML_DOCDIR}" ]]; then pushd "${GAP_PKG_HTML_DOCDIR}" > /dev/null || die local docdir="$(gap-pkg_dir)/${GAP_PKG_HTML_DOCDIR}" insinto "${docdir}" # See above for f in *.{htm,html,css,js,png}; do doins "${f}" dosym -r "${docdir}/${f}" "/usr/share/doc/${PF}/html/${f}" done popd > /dev/null || die fi # Any GAP source files that live in the top-level directory. insinto $(gap-pkg_dir) for f in *.g; do doins "${f}" done # We're done globbing eshopts_pop # The gap and lib dirs that usually also contain GAP code. [[ -d gap ]] && doins -r gap [[ -d lib ]] && doins -r lib # Any additional user-specified files or directories. for f in "${GAP_PKG_EXTRA_INSTALL[@]}"; do doins -r "${f}" done # The bin dir, that contains shared libraries but also sometimes # regular executables in an arch-specific subdirectory. We do # this last because it messes with insopts -- doexe doesn't work # recursively and we don't care what the subdirectory structure is. if [[ -d bin ]]; then insopts -m0755 doins -r bin # Find and remove .la files from this package's bindir. The # usual "find" command doesn't work here because occasionally we # find *.la files in GAP packages that are not libtool archives # and should not be deleted. find "${ED}$(gap-pkg_dir)/bin" -type f -name '*.la' -delete || die fi } fi EXPORT_FUNCTIONS src_configure src_compile src_test src_install