This chapter explains the basics of how to write a GAP package so that it interfaces properly to GAP. For the role of GAP packages and the ways to load them, see Chapter GAP Packages in the GAP Reference Manual.
There are two basic aspects of creating a GAP package. First, it is a convenient possibility to load additional functionality into GAP including a smooth integration of the package documentation. And secondly, a package is a way to make your code available to other GAP users. The GAP Team provides some help with the distribution of packages. In particular, a package can be submitted to a refereeing process. Check out the GAP Web pages http://www.gap-system.org for more details.
We start this chapter with a description how the directory structure of a GAP package must look like and then add remarks on certain aspects of creating a package, some of these only apply to some packages.
All files of a GAP package must be collected in a single directory.
To use the package with GAP this directory must be a subdirectory
of a pkg
directory in (one of) the GAP root directories (see
GAP Root Directory in the GAP Reference Manual).
(For example, if GAP is installed in
/usr/local/gap4
then put the files of your package MyPack
in the
directory /usr/local/gap4/pkg/mypack
.) Let us call this directory
the home directory of the package.
There are three file names with a special meaning in the home
directory of a package: PackageInfo.g
and init.g
which must be
present and read.g
which is optional.
The file PackageInfo.g
contains meta-information about the package
(package name, version, author(s), relations to other packages,
homepage, download archives, banner, ...). This is used by the package
loading mechanism and also for the distribution of a package to other
users. The content of this file is explained via a template file
below (see The PackageInfo.g File).
The init.g
is read when the package is loaded (see
LoadPackage in the GAP Reference Manual).
In principle this file could contain the whole
GAP code of a package, but usually it contains mainly Read
or
ReadPackage
statements for reading further files of the package. For
many packages it may be useful to have declaration and implementation
parts in different files, see Declaration and Implementation Part
below for more details. In that case it can be useful to read in only
the declaration parts from the init.g
file and to add
a file read.g
which contains the ReadPackage
statements for the
implementation parts.
There is one further rule for the location of kernel library modules or external programs which is explained in Installation of GAP Package Binaries below.
All other files can be organized as you like. But we suggest that you
have a look at existing packages and use a similar scheme. For
example, collect your GAP code in files in a subdirectory lib
or
gap
, put the documentation in a subdirectory doc
, put source code
for compilation in src
, data libraries in extra subdirectories and
so on.
If you intend to make your package available to other users it is essential to include a documentation how to install and use your programs.
Concerning the installation you should produce a file README
which
gives a short description of the purpose of the package and contains
proper instructions how to install your package. Again, check out some
existing packages to get an idea how this could look like.
Concerning the documentation of the use of the package there are currently two recognised ways of producing GAP package documentation. There is the method that has been used to produce the main manuals for GAP which requires the documentation to be written in TeX according to the format described in Chapter The gapmacro.tex Manual format. There is also an XML-based documentation format that is defined in and can be used with the GAPDoc package (see GAPDoc:Introduction and Example).
In principle it is also possible to use some completely different
documentation format. In that case you need to study the
Chapter Interface to the GAP Help System to learn how to make your
documentation available to the GAP help system. There should be at
least a text version of your documenation provided for use in
the terminal running GAP and some nicely printable version in
.dvi
and/or .pdf
format. Many GAP users like to browse the
documentation in HTML-format via their Web-browser.
We illustrate the creation of a GAP package by an example of a basic package.
Create the following directories in your home area: pkg
and
pkg/test
. Inside the directory test
create an empty file init.g
,
and a file PackageInfo.g
with the following contents.
SetPackageInfo( rec( PackageName := "test", Version := "1.0", AvailabilityTest := ReturnTrue, Autoload := false, BannerString := Concatenation( [ "#I loading the GAP package ``test'' in version ", ~.Version, "\n" ] ), PackageDoc := rec( BookName := "test", SixFile := "doc/manual.six", Autoload := true ) ) );
This file declares the GAP package with name ``test'' in version
1.0. There are no requirements that have to be tested, so ReturnTrue
(see ReturnTrue in the GAP Reference Manual) is used as test
function. The package is not autoloaded, and it has its individual
banner string. The package documentation consists of one autoloaded
book; the SixFile
component is needed by the GAP help system.
Now start GAP with the command
gap -l "./;"
(the -l "./;"
option adds the current directory to the GAP root
directories and allows GAP to find the packages installed in the
./pkg
directory.
gap> LoadPackage("test"); #I loading the GAP package ``test'' in version 1.0 true
This GAP package is too simple to be useful, but we have succeeded
in loading it via LoadPackage
.
If you want to distribute your package you should create a WWW
homepage containing some basic information, archives for download and
the README
file with installation instructions, and maybe a copy of
the packages PackageInfo.g
file.
The responsibility for this WWW homepage is with the package authors/maintainers.
If you tell us about your package (say, by mail to
support@gap-system.org
) we may agree to add a link to your package
homepage from the GAP website and to redistribute the current
version of your package via the GAP download sites. We can also
provide some service for producing several archive formats from the
archive you provide (e.g., you produce a .tar.gz
version of your
archive and we produce also a .tar.bz2
, a .zoo
and a -win.zip
version from this).
Please, consider to submit your package to the GAP package refereeing process.
We suggest to create a PackageInfo.g
file for your package by
copying the one in the Example
package, distributed with GAP, and
to adjust it for your package. Within GAP you can look at that file
by
Pager(StringFile(Filename(DirectoriesLibrary(), "../pkg/example/PackageInfo.g")));
As a first step the example in An Example of a GAP Package shows the
information needed for the package loading mechanism of a simple
package. If your package depends on the functionality of other
packages, the component Dependencies
given in the PackageInfo.g
file becomes important, see Requesting one GAP Package from within Another below.
The other entries become relevant if you want to distribute your package: they contain lists of authors and/or maintainers including contact information, URLs of the package archives and README files, status information, text for a package overview Web page, and so on. See the mentioned template file for a list and explanation of all recognized entries.
Once you have created the PackageInfo.g
file for your
package, you can test its validity with the command
ValidatePackageInfo(filename);
.
It is possible for one GAP package A
, say, to require another package
B
.
For that, one simply adds the name and the (least) version number of the
package B
to the NeededOtherPackages
component of the Dependencies
component of the PackageInfo.g
file of the package A
.
In this situation, loading the package A
forces that also the package
B
is loaded, and that A
cannot be loaded if B
is not available.
If B
is not essential for A
but should be loaded if it is available
(for example because B
provides some improvements of the main system
that are useful for A
)
then the name and the (least) version number of B
should be added to the
SuggestedOtherPackages
component of the PackageInfo.g
file of A
.
In this situation, loading A
forces an attempt to load also B
,
but A
is loaded even if B
is not available.
When GAP packages require each other in a circular way,
a ``bootstrapping'' problem arises of defining functions before they are
called.
The same problem occurs in the GAP library, it is resolved there
by separating declarations (which define global variables such as
filters and operations)
and implementations (which install global functions and methods)
in different files.
Any implementation file may use global variables defined in any declaration
file.
GAP initially reads all declaration files (in the library they have a
.gd
suffix) and afterwards reads all implementation files
(which have a .gi
suffix).
Something similar is possible for GAP packages:
If a file read.g
exists in the home directory of the package,
this file is read only after all the init.g
files of all (implicitly)
required GAP packages are read.
Thus one can separate declaration and implementation for a GAP package
in the same way as done for the GAP library,
by creating a file read.g
, restricting the ReadPackage
statements in
init.g
to only load those files of the package that provide declarations,
and to load the implementation files from read.g
.
See Section Declaration and Implementation Part in the Programmers'
Tutorial which discusses further the commands that should appear in the
declaration part (i.e., in the files read with ReadPackage
from init.g
)
and in the implementation part (i.e., in the files read with ReadPackage
from read.g
) of a package.
GAP packages that involve stand-alone programs are fundamentally different from GAP packages that consist entirely of GAP code.
This difference is threefold: A user who installs the GAP package must also compile (or install) the package's binaries, the package must check whether the binaries are indeed available, and finally the GAP code of the package has to start the external binary and to communicate with it. We will treat these three points in the following sections.
If the package does not solely consist of an interface to an external binary and if the external program called is not just special-purpose code, but a generally available program, chances are high that sooner or later other GAP packages might also require this program.
We therefore strongly suggest to provide a documented GAP function that will call the external binary. We also suggest to create actually two GAP packages; the first providing only the binary and the interface and the second (requiring the first, see Requesting one GAP Package from within Another) being the actual GAP package.
The scheme for the installation of package binaries which is described further on is intended to permit the installation on different architectures which share a common file system (and share the architecture independent file).
A GAP package which includes external binaries contains a bin
subdirectory. This subdirectory in turn contains subdirectories for
the different architectures on which the GAP package binaries are
installed. The names of these directories must be the same as the
names of the architecture dependent subdirectories of the main bin
directory. Unless you use a tool like autoconf
yourself, you must
obtain the correct name of the binary directory from the main GAP
branch. To help with this, the main GAP directory contains a file
sysinfo.gap
which assigns the shell variable GAParch
to the proper
name as determined by GAP's configure
process. For example on a
Linux system, the file sysinfo.gap
may look like this:
GAParch=i586-unknown-linux2.0.31-gcc
We suggest that your GAP package contains a file configure
which
is called with the path of the GAP root directory as
parameter. This file then will read sysinfo.gap
and set up
everything for compiling under the given architecture (for example
creating a Makefile
from Makefile.in
.
The standard GAP distribution contains a GAP package ``example'' whose installation script shows an example way of how to do this.
If an external binary is essential for the workings of a GAP package,
the function stored in the component AvailabilityTest
of the
PackageInfo.g
file of the package should test whether the program
has been compiled on the architecture (and inhibit package loading
if this is not the case).
This is especially important if the package is loaded automatically.
The easiest way to accomplish this is to use Filename
(see Filename in the GAP Reference Manual) for checking for the
actual binaries in the path given by DirectoriesPackagePrograms
(see DirectoriesPackagePrograms in the GAP Reference Manual)
for the respective package.
For example the ``example'' GAP package could use the following commands
to test whether the binary hello
has been compiled;
they issue a warning if not and will only load if it is indeed available.
... AvailabilityTest := function() local path,file; # test for existence of the compiled binary path:=DirectoriesPackagePrograms("example"); file:=Filename(path,"hello"); if file=fail then Info(InfoWarning,1, "Package ``example'': The program `hello' is not compiled"); Info(InfoWarning,1, "`HelloWorld()' is thus unavailable"); Info(InfoWarning,1, "See the installation instructions; ", "type: ?Installing the Example package"); fi; return file<>fail; end, ...
(In fact the AvailabilityTest
function that is actually used
in the ``example'' package always returns true
,
just the warnings are printed if the binary is not available.
This means that the binary is not regarded as essential for this
package.)
You might also have to cope with the situation that external binaries will only run under UNIX (and not, say on a Macintosh). See Testing for the System Architecture in the GAP Reference Manual for information on how to test for the architecture.
There are two reasons for this: the input data has to be passed on to the stand-alone program and the stand-alone program has to be started from within GAP.
There are two principal ways of doing this.
The first possibility is to write all the data for the stand-alone to
one or several files, then start the stand-alone with Process
or
Exec
(see Process and Exec in the GAP Reference
Manual) which then writes the output data to file, and finally read in
the standalone's output file.
The second way is interfacing via iostreams (see Section Input-Output Streams in the GAP Reference Manual). The support for this is in its infancy.
Reading a larger package can take a few moments and will take up user workspace. This might be a nuisance to users, especially if the package is loaded automatically. The same problem of course affects the GAP library, the problem there is solved using completion files (see Completion Files in the GAP Reference Manual).
Completion files make it possible to read only short function headers initially which are completed to full functions only when the functions are actually called. This section explains how to set up completion for a GAP package.
Completion works for those files which are read (using ReadPackage
)
from the read.g
file. (This is no real restriction as completion affects
only the implementation part.)
To create completion files, load the GAP package,
and then use the following command.
CreateCompletionFilesPackage(
pkgname )
This will create a new file read.co
in the home directory of the
loaded version of the GAP package pkgname
(so you must have write permissions there).
When the GAP package is loaded, this file is used in place of read.g
,
and automatically takes care of completion.
When you change files which are completed, GAP will complain about
non-matching CRC files and will not load them.
In this case simply remove the read.co
file and create it anew.
As a GAP package author you should consider including a completion file with the package.
If you start GAP with the command line option -D
, it displays
information about reading and completion, the command line option -N
turns
completion off (as if all .co
files were erased).
(Section Advanced Features of GAP in the GAP Reference Manual
describes the options -D
and -N
.)
The completion mechanism is designed for delaying reading function bodies whose headers has to be read when the package is loaded; this is necessary for example for the installation of methods.
For files not containing functions --this applies to many data files-- completion does not help. However, another mechanism allows one to delay reading such files until the data are actually accessed.
DeclareAutoreadableVariables(
pkgname,
filename,
varlist )
Let pkgname be the name of a package,let filename be the name of
a file relative to the home directory of this package,
and let varlist be a list of strings that are the names of global
variables which get bound when the file is read.
DeclareAutoreadableVariables
notifies the names in varlist such that
the first attempt to access one of the variables causes the file to be
read.
A version number is a string which contains nonnegative integers separated by non-numeric characters. Examples of valid version numbers are for example:
"1.0" "3.141.59" "2-7-8.3" "5 release 2 patchlevel 666"
Version numbers are interpreted as lists of integers and are compared
in that way. Thus version "2-3"
is larger than version "2-2-5"
but
smaller than "11.0"
.
It is possible for code to require GAP packages in certain versions. In this case, all versions, whose number is equal or larger than the requested number are acceptable. It is the task of the package author to provide upwards compatibility.
The global variable GAPInfo.Version
contains the version number of
the version of GAP and also can be checked against (using
CompareVersionNumbers
, see CompareVersionNumbers in the GAP
Reference Manual).
Package authors should choose a version numbering scheme that allows a new version number even after tiny changes to the package. The automatic update of package archives in the GAP distribution will only work if a package becomes a new version number.
The releases of GAP packages are independent of releases of GAP. Therefore GAP packages should be wrapped up in separate files that can be installed onto any version of GAP. Similarly a GAP package can be upgraded any time without the need to wait for new releases of GAP.
Because it is independent of the version of GAP a GAP package should be
archived from the GAP pkg
directory, that is all files are archived with
the path starting the package's name.
The archive of a GAP package should contain all files necessary for the
package to work. In particular there should be a compiled documentation,
which includes the manual.six
, manual.toc
and manual.lab
file in the
documentation subdirectory which are created by TeXing the documentation,
if you use the gapmacro.tex
or GAPDoc document formats. (The
first two are needed by the GAP help system, and the manual.lab
file is
needed if the main manual is referring to your package. Use the command
GAPDocManualLab( packagename );
to create this file for your help books if
you use GAPDoc.)
If the package provides a substantial amount of code, especially if it is intended to be loaded automatically, create a completion file (see Package Completion) and include it with the package.
Currently, the GAP distribution provides archives in four different formats.
.tar.gz
, a standard UNIX tar
archive, compressed with gzip
.tar.bz2
, a standard UNIX tar
archive, compressed with bzip2
.zoo
, a special version of zoo
archives, that can essentially
be used on all operating systems with the unzoo
utility provided with
the GAP distribution
-win.zip
, an archive in zip
format, where text files should
have DOS/Windows style line breaks
For convenience of possible users it is sensible that you archive your package also in one or several of these formats.
For packages which are redistributed via the GAP Web site, we offer an automatic conversion of any of the formats listed above to all the others.
[Top] [Up] [Previous] [Next] [Index]
GAP 4 manual
March 2006