-
Notifications
You must be signed in to change notification settings - Fork 162
GAP buildsystem
This document is meant to be a design guideline for the new GAP build system. To this end, it
- summarizes requirements the new build system should satisfy
- discusses various off-the-shelf build tools out there
- ponders how to deal with package (including those which contain kernel extensions or standalone binaries)
- ...
The thoughts in here then will be used to drive the implementation of the actual new build system (and in turn, this development will lead to updates in this document).
Any questions, concerns or other feedback should for now be directed at Max Horn.
The following minimal requirements were assumed for the new build system. We do not present a rationale for them; if you have doubts about any, please bring them up on the mailing list or as an issue.
- MUST work flawlessly on major Linux distributions (Ubuntu, Fedora, ...), major *BSD variants, Mac OS X
- SHOULD work on any POSIX compatible system
- SHOULD work on Windows with Cygwin
- MAY work on Windows without Cygwin (we really would like good windows support overall, but without any experienced Windows developers to help out, this may be difficult anyway; thus non-Cygwin Windows support is not a priority right now)
-
MAY require that user and developers have:
- a working C compiler supporting ISO C99 or newer
- a working C++ compiler
- basic POSIX environment, including sh, make, rm, mkdir, ...
- GNU make (if so, make sure it works with gmake on *BSD)
- Python
- ... ?
-
MAY require that developers install certain additional common tools, such as autoconf, ruby, ...
-
SHOULD NOT require users to install additional "exotic" tools.
- That is, tools which are not shipped with the system resp. are easily available via a systems standard package management procedures. Thus,
- for OS X, tools should ideally be supplied with Xcode;
- for Ubuntu, they should be installable for the most recent LTS release, which is 14.04;
- and so on).
- This means that e.g.
cmake
is at least problematic, because it requires users to havecmake
in order to compile GAP, not just developers - However, we could considerably weaken this requirement if we managed to consistently provide up-to-date pre-built GAP binaries to users for at least Linux, Mac OS X and Windows. (Bonus point for additional Unix variants). Also, pre-made virtual machines and docker images.
- for Mac OS X, Max has something that could perhaps be used as a basis, see https://github.com/fingolfin/gap-osx-bundle and https://github.com/fingolfin/gap-osx-binary;
- for Linux, Frank's rsync distribution could be a good model (in terms of a relocatable and statically linked set of binaries).
- for Windows, we are probably already in good shape thanks to Alex' efforts.
- however, details of this should be discussed elsewhere
- That is, tools which are not shipped with the system resp. are easily available via a systems standard package management procedures. Thus,
-
Support for out-of-tree builds.
- So that one can compile multiple configurations from the same GAP sources (e.g. 32 and 64 bit build; with and without readline; builds for different architectures; ...)
-
Support parallel builds (e.g. via
make -jN
)- must be correct (not always the case, e.g. for naive Makefiles)
-
Provide proper dependency tracking
- between .c and .h files
- but also if e.g. parts of the build system get changed, the right thing should happen
-
changing build options and then building must work correctly.
- build options could be e.g. debug mode on/off; HPC-GAP mode; etc.
- Here, "work correctly" means that the equivalent of typing
make
needs to rebuild all files that are affected by the change; i.e. the result ofmake
andmake clean && make
should be fully equivalent.
-
Maintainability, ease-of-use: It should be easy or "obvious" to add new source file, and perform other routine changes on the build system. Ideally, also more complicated changes shouldn't be too hard (or at least be thoroughly documented).
-
It should be easy to build GAP against system-wide installations of GMP, readline, and other future dependencies
-
Reimer also wants (as far as I understood) support for doing
make CFLAGS='-some custom flags'
which should automatically detect the changed flags, and rebuild everything accordingly.- While this would be nice to have, there are alternatives, e.g. having multiple out-of-tree build directories, one for each set of configuration one wants (typically, I would have one "release" build dir with default settings, and a "debug" build dir, and perhaps also "release32" and "debug32" for debug builds; with HPC-GAP merged, there might also be "hpcgap", "hpcgap-debug", ... )
- If we want this, then the build system of
git
does something like this purely based on GNU make.
-
Replicating the current system of "build configurations" exactly
- In the current system, one can build multiple GAP "configs" inside a single GAP source directory, for multiple architectures, in multiple configurations *Instead, out-of-tree builds (the "industry standard" solution for this problem) will be used.
-
Guessing "perfect" build options, in particular optimizer settings
- this is a doomed effort. Instead, make it as simple as possible (and in particular: as "standard" as possible) for users to customize these.
- So there would be no replacement for the
GP_CFLAGS
macro fromcnf/aclocal.m4
-
Support for multiple architectures "in the same directory". This might be annoying for people who use a single GAP source directory on different machines with different architectures, e.g. shared over a network drive (e.g. Laurent seems to be doing that). However, it seems difficult to achieve such a thing with standard tools. As a workaround, affected users (who I'd consider to be "power users") could use different out-of-tree builds for the various architectures, and then e.g. a shell script which invokes the right GAP build based on
uname
.
This refers to the combination of the following three tools:
- GNU autoconf - configuration detection
- GNU automake - generates Makefiles, requires POSIX shell and m4
- GNU libtool - portable generation of shared libraries and loadable modules
Pros:
- widely available, most package managers provide these (not bundled on Mac OS X, yet easily installable by hand, or via Fink/Hombrew/MacPorts)
- for users, the requirements are minimal: basically, only bourne shell is needed (possibly also GNU make, but it checks for that)
- support a great variety of systems, including rather exotic ones
- has been the "industry standard" for at least two decades
Cons:
- widely disliked for being "too complicated" (however, this is often based on outdated facts, as e.g. documentation has improved considerably in recent years; see e.g. Autotools Mythbuster)
- the build system input files (
Makefile.am
,configure.ac
) may be relatively clear and concise, but they are then turned in big convoluted files (Makefile.in
,configure
) which are hard to understand and debug.
Pros:
- Windows support
- ... TODO
Cons:
- requires users to install the 'cmake' tool
- ... TODO
Pros:
- ... TODO
Cons:
- ... TODO
autosetup https://msteveb.github.io/
Pros:
- ... TODO
Cons:
- ... TODO
?
?
TODO: take inspiration from other projects, e.g. git
TODO: also look at what I wrote for ScummVM
and pentagram
?
We considered a great variety of other build system tools. The tools we considered were based on
- personal experience
- discussions with other GAP team members
- some internet search, and then in particular the following pages:
However, a great number of them were immediately removed from further consideration because they were
- apparently unmaintained,
- had an unclear future (rule of thumb: don't expect a 1 year old project to be still maintained in 1 year),
- were lacking in documentation,
- were lacking in portability (and worryingly, often even a concern or understanding for it),
- were too low-level,
- conflicted with one of the must-have requirements
- or a combination thereof.
However, if you think there is a build tool that really should be considered, please let me know.
Whatever tools we pick, the new build system should preserve various current features, and also additional features that we are badly missing in the current system.
In the following subsections, I try to summarize these concerns. This will of course influence the final choice of tools, but also is a good reference list for whoever implements the build system, to make sure they are not forgetting something by accident.
- TODO: discuss how "plain" packages without anything compiled fit in. in particular, how does GAP locate them. Probably exactly the same way as now?
The new GAP build system has implications for GAP packages which build kernel extensions (and, to a lesser degree, packages which build binaries).
- Packages that provide a kernel extension include:
- Browse
- cvec
- datastructures
- digraphs
- edim
- ferret
- float
- Gauss
- io
- json
- linboxing
- NormalizInterface
- orb
- profiling
- zeromq
- Packages that provide and use a standalone executable include:
- ace
- anupq
- grape
- guava
- homology
- kbmag
- nq
- xgap
Thus, we should think about ...
- ... providing a
compatibility mode
which somehow allows existing, unmodified, packages to be used with the new GAP build system; - ... documenting a migration strategy for package authors to upgrade their package's build system to work natively with the new GAP build system, and even take advantage of its improvements.
Providing a compatibility mode
would allow for an incremental
migration to the new system. There are quite some packages with kernel
extensions, and I don't think it is realistic that they all switch to
the new system at once. Moreover, without an incremental strategy, when
exactly should package authors update their build system? It would have
to be done after we switched to the new build system, but before there
was an actual release. This would require an extraordinary amount of
coordination for all involved.
Hence, I think we really should go for a compatibility mode
.
One rough idea would be to use some cleverly created symlinks and text files inside an out-of-tree build directory to fake an "old style" GAP root directory.
- in the following,
$SRCDIR
is the path to the GAP source directory, and$BUILDDIR
the path to an out-of-tree build directory. - first generate a "unique" GAParch (e.g. take
config.guess
output, then append a random string / a UUID; or perhaps the hash of the build options). This way, one can still use a single package with multiple different GAP builds. - create a fake "outer"
sysinfo.gap
containing something like this:
GAParch=UNIQUE_GAPARCH
GAParch_system=UNIQUE_GAPARCH-t
GAParch_abi=64-bit
Perhaps we can also get rid of GAParch_abi (only linboxing and homology seem to use it, and both seem to need updates anyway in order to work, even with our old build system)
- create a bin directory
$BUILDDIR/bin/UNIQUE_GAPARCH
- create a fake inner
sysinfo.gap
(?) - add symlinks for
lib
,src
etc. inside$BUILDDIR
which point to$SRCDIR/src
,$SRCDIR/lib
etc. - ... TODO: flesh this out.
TODO: describe how new package build systems could look like
TODO: we need to write both a document that explains to package authors how their build system should "hook" into GAP; and also should provide a default / "example" package build system doing just that (or possibly two; say one based on a super-simple Makefile plus gac
or gap-config
; the other based on whatever build system we end up using for GAP itself)
TODO: Rough idea for convenient buildin of packages: they provide a build.sh
script.
The user invokes this script, with (optionally) the path of the GAPROOT to use.
The build script could do something similar to this (example is based on autoconf)
SRCDIR=`pwd`
BUILDDIR="$GAPROOT/pkg/$PKGNAME"
# TODO: perform test whether dir is valid and writeable
mkdir -p "$BUILDDIR"
cd "$BUILDDIR"
"$SRCDIR/configure"
make
TODO: can we arrange things so that if you type LoadPackage("io")
, but io
has not yet been compiled, then GAP somehow figured out how to compile your package, does that for you, and then loads it (and of course it should do that for dependencies, too)? That would be a good step towards a package manager
- TODO:
- these should be a bit easier than those with kernel extensions
- in particular, they are independent of the build config options GAP uses. So, a 64 bit GAP in debug mode could of course use a 32bit release mode standalone binary. Indeed, this may often be desirable
- Hence it may be desirable to share a single standalone binary between different GAP configurations / out-of-tree builds.
-
extend GAP to be more clever when it comes to finding its library: namely, try some strategies:
- look for a pkg dir relative to the directory the GAP binary itself is in
- in the current build system, the natural place is ../../pkg
- in the new build system, it might be .
- also hardcode a PATH into the binary
- in the current build system, it might be the GAPROOT from which the build was started
- in the new build system, it might be the GAPROOT derived from the prefix (e.g. something like /usr/share/gap; possibly multiple spots, e.g. /usr/share/gap/lib for the GAP library code, vs. /usr/lib/gap/ for kernel extensions
- look for a pkg dir relative to the directory the GAP binary itself is in
-
support for installing GAP via (the analogue of)
make install
; this would install...-
GAPROOT/bin/gap.sh
ORGAPROOT/bin/ARCH/gap
intoPREFIX/bin
- dirs not containing compiled stuff (lib, small, trans, perf, doc, ...) into e.g.
PREFIX/share/gap
- the actual binary and / or *.o files into
PREFIX/lib/gap
orPREFIX/libexec/gap
- packages would probably still go into
PREFIX/share/gap/pkg
, but their binaries (if any) would go into a separate dir, sayPREFIX/lib/gap/pkg
(luckily, we already abstract away the way packages find their binaries, so that shouldn't be too hard) - headers could either go into
PREFIX/includes/gap
(more "standard-y"), or intoPREFIX/share/gap/src
(makes it easier to provide backwards compatibility) - ... though if there is something like
config.h
, this needs special consideration (it cannot go intoPREFIX/share/
, as that is reserved for files which are architecture independent) - to support building packages which expect all of GAP to be in a single directory, there could be a directory which creates a fake "classic" GAPROOT via some symlinks
-
-
On Mac OS X, there is by default no GNU readline and no GMP. So do we keep bundling GMP, and perhaps also bundle readline? Or do we expect people to install these via Fink / MacPorts / homebrew? If we do the latter, this might affect once again our choice of build tools...
-
support custom CLFAGS etc. being passed to Make ???
-
provide an alternative for "gac"
- determine what that exactly means
- do we want to keep supporting static linked kernel extensions?
-
when building kernel extensions, one has two options:
- point the extension's build system at the GAP source folder: then this GAP path is hardcoded in the extension. THis is what GAP developers would use
- when pointed to a system installed GAP (say in /usr/local or /usr or /opt/GAP/ or ...) then of course that path is hard coded
-
compile time switch between HPC-GAP and GAP
-
GAP package support:
- it should be easy to write GAP packages with kernel extensions based on the new build system
- e.g. keep providing
gac
or something like it - provide a
gap-config
script and/or agap.pc
file for pkg-config which enable package authors to easily detect the "active" GAP root etc. (this should be mostly interesting for systems where "make install" was used to install GAP globally)
-
collect some thoughts on a package manager here, too?
- Simply ask every package to provide a special script, say build.sh, in its root directory, which we promise to call with several parameter (e.g. path to GAP installation, possible also to the gap binary; the arch type; maybe more).
- use ReadWeb() or SingleHTTPRequest() from IO to download the latest .tar.gz of a package, then unpack it locally, and (using the above) build the package. Finally, tell the user to restart GAP for the update to take effect.
- Or write a small GAP package that uses libcurl and libarchive to download archives and extract them (even on Windows). Perhaps more cleanly, provide wrapper packages for libcurl and libarchive.
-
It would be super cool if we could support
git bisect
for revisions before the build system change. Not sure whether that is feasible, though...- perhaps the build system should detect if we switched to a commit with the old build system, and in that case, refrain from reseting itself (i.e. no calls to config.status, if we are using autoconf)
- that would require extensive testing, of course
-
if we end up using
config.guess
output anywhere, I want a special case for Mac OS X: There,config.guess
outputs something like e.g.x86_64-apple-darwin15.3.0
for Mac OS 10.11.3 (the 15.3 corresponds to 11.3 -- but e.g. OS X 10.8.5 is 12.6 and not 12.5; anyway....). This is quite annoying because it means e.g. OS X 10.11.2 and 10.11.3 are effectively treated as binary incompatible, which they are not. Solution: Strip the last ".N.N" bits from the arch name. -
if we use autoconf, let's reduce the tests that configure performs to the bare minimum. There is no point in e.g. checking for the presence of
stdio.h