summaryrefslogtreecommitdiff
path: root/eclass/gap-pkg.eclass
blob: 9efeb2e8f9b96f5ea42cbf2297a2dbadd723f666 (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
# Copyright 2024 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

# @ECLASS: gap-pkg.eclass
# @MAINTAINER:
# François Bissey <frp.bissey@gmail.com>
# Michael Orlitzky <mjo@gentoo.org>
# Gentoo Mathematics Project <sci-mathematics@gentoo.org>
# @AUTHOR:
# François Bissey <frp.bissey@gmail.com>
# Michael Orlitzky <mjo@gentoo.org>
# @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