Goto Chapter: Top 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 Bib Ind
 [Top of Book]  [Contents]   [Previous Chapter]   [Next Chapter] 

76 Using and Developing GAP Packages
 76.1 Installing a GAP Package
 76.2 Loading a GAP Package
 76.3 Citing a GAP Package
 76.4 Structure of a GAP Package
 76.5 The PackageInfo.g File
 76.6 Package Dependencies (Requesting one GAP Package from within Another)
 76.7 External Dependencies (System packages needed by a GAP Package)
 76.8 Extensions Provided by a Package
 76.9 Declaration and Implementation Part of a Package
 76.10 Standalone Programs in a GAP Package
 76.11 Kernel Modules in GAP Packages
 76.12 Testing a GAP package

76 Using and Developing GAP Packages

The functionality of GAP can be extended by loading GAP packages. The GAP distribution already contains all currently redistributed GAP packages in the gap-4.17dev/pkg directory.

GAP packages are written by (groups of) GAP users who may not necessarily be members of the GAP developer team. The responsibility and copyright of a GAP package remains with the original author(s).

GAP packages have their own documentation which is smoothly integrated into the GAP help system. (When GAP is started, LoadPackageDocumentation is called for all packages.)

All GAP users who develop new code are invited to share the results of their efforts with other GAP users by making the code and its documentation available in form of a package. How to create a package is described on the GAP website at https://www.gap-system.org/packages/create/, and how to get it distributed with GAP at https://www.gap-system.org/packages/submit/. The GAP package Example (see https://github.com/gap-packages/example) shows what a complete package looks like.

This chapter first describes how to install, load and cite existing packages, and then how packages work: the files a package consists of, its PackageInfo.g file, dependencies between packages, packages with compiled code, and how packages are tested.

76.1 Installing a GAP Package

Before a package can be used it must be installed. A standard distribution of GAP already contains all the packages currently redistributed with GAP. This set of packages has been checked for compatibility with the system and with each other during release preparation. Most of the packages can be used immediately, but some of them may require further installation steps (see below).

GAP packages are released independently of the main GAP system, so it can be useful to upgrade a package, or to install one that is not distributed with GAP, without upgrading your GAP installation.

The easiest way to do this is the PackageManager package (see https://github.com/gap-packages/PackageManager), which is distributed with GAP and loaded by default. For example,

gap> InstallPackage( "digraphs" );

downloads the current version of the package Digraphs, installs it together with the packages it needs, and compiles it if necessary. Instead of a package name, InstallPackage (PackageManager: InstallPackage) also accepts the URL of a package archive, of a git repository, or of a PackageInfo.g file. UpdatePackage (PackageManager: UpdatePackage) and RemovePackage (PackageManager: RemovePackage) update and remove a package installed this way. The packages are installed in the pkg subdirectory of GAPInfo.UserGapRoot (see 9.2), so no write access to the main GAP installation is needed.

A package can also be installed by hand. It consists of a collection of files within a single directory that must be a subdirectory of the pkg directory in one of the GAP root directories (see 9.2). If you don't have access to the pkg directory in your main GAP installation you can add private root directories as explained in that section.

Whenever you download or clone an archive of a GAP package, it will contain a README file (or README.md etc.) that explains how it should be installed. Some packages just consist of GAP code and the installation is done by unpacking the archive in one of the places described above. There are also packages that need further installation steps, such as compilation or installing additional software to satisfy their dependencies. If there are some external programs which have to be compiled, this is often done by executing ./configure; make inside the unpacked package directory (but check the individual README files).

Most of the packages that require compilation can be compiled in a single step by changing to the pkg directory of your GAP installation and calling the ../bin/BuildPackages.sh script.

Note that if you use Windows you may not be able to use some or all external binaries.

76.2 Loading a GAP Package

If a package is not already loaded, it may be loaded using the function LoadPackage (76.2-1).

Some GAP packages are prepared for automatic loading, that is they will be loaded automatically when GAP starts (see 76.2-2).

Whether a package can be loaded, whether it has been loaded, and in which version, is reported by TestPackageAvailability (76.2-7), IsPackageLoaded (76.2-8) and InstalledPackageVersion (76.2-9).

76.2-1 LoadPackage
‣ LoadPackage( name[, version][, banner] )( function )

loads the GAP package with name name.

As an example, the following loads the GAP package SONATA (case insensitive) which provides methods for the construction and analysis of finite nearrings:

gap> LoadPackage("sonata");
... some more lines with package banner(s) ...
true

The package name is case insensitive and may be appropriately abbreviated. At the time of writing, for example, LoadPackage("semi"); will load the Semigroups package, and LoadPackage("js"); will load the json package. If the abbreviation cannot be uniquely completed, a list of available completions will be offered, and LoadPackage returns fail. Thus the names of all installed packages can be shown by calling LoadPackage("");.

When the optional argument string version is present, the package will only be loaded in a version number equal to or greater than version (see CompareVersionNumbers (76.5-3)). If the first character of version is = then only that version will be loaded.

LoadPackage will return true if the package has been successfully loaded, and will return fail if the package could not be loaded. The latter may be the case if the package is not installed, if necessary binaries have not been compiled, or if the version number of the available version is too small. If the package cannot be loaded, TestPackageAvailability (76.2-7) can be used to find the reasons. Also, DisplayPackageLoadingLog (76.2-6) can be used to find out more about the failure. To see the problems directly, one can change the verbosity using the user preference InfoPackageLoadingLevel, see InfoPackageLoading (76.2-6) for details.

If the package name has already been loaded in a version number equal to or greater than version, LoadPackage returns true without doing anything else.

If the optional argument banner is present then it must be either true or false; in the latter case, the effect is that no package banner is printed.

If the global option OnlyNeeded is given, as in LoadPackage("sonata" : OnlyNeeded), then the suggested packages of name and, recursively, of its dependencies are not loaded. This is meant for checking that a package works without its suggested packages, see Section 76.6.

After a package has been loaded, all its code becomes available to use with the rest of the GAP library.

Load all packages you need at the start of a session, before doing any computations. Loading a package can install new methods and thus change which methods get selected, so results can depend on whether they were computed before or after the package was loaded. For the same reason, code should not call LoadPackage inside its functions, see Section 76.6.

76.2-2 Automatic loading of GAP packages

When GAP is started some packages are loaded automatically, and these belong to two categories. The first are those packages which are needed to start GAP (at the present time, the only such package is GAPDoc). Their list is contained in GAPInfo.Dependencies.NeededOtherPackages. The second are packages which are loaded during GAP startup by default. The latter list may be obtained by calling UserPreference("PackagesToLoad") and is customisable as described in Section Reference: Configuring User preferences.

While GAP will not start if any of the packages from the former group is missing, loading of the packages from the latter group may be suppressed by using the -A command line option (see 3.1).

If for some reason you don't want certain packages to be automatically loaded, GAP provides three levels for disabling autoloading.

The autoloading of specific packages can be overwritten for the whole GAP installation by putting a file NOAUTO into a pkg directory that contains lines with the names of packages which should not be automatically loaded.

Furthermore, individual users can disable the autoloading of specific packages by putting the names of these packages into the list that is assigned to the user preference ExcludeFromAutoload, for example in the user's gap.ini file (see 3.2-1).

Using the -A command line option when starting GAP (see 3.1), automatic loading of packages is switched off for this GAP session.

In any of the above three cases, the packages listed in GAPInfo.Dependencies.NeededOtherPackages are still loaded automatically, and an error is signalled if any of these packages is unavailable.

See SetPackagePath (76.2-3) for a way to force the loading of a prescribed package version. See also ExtendRootDirectories (76.2-4) and ExtendPackageDirectories (76.2-5) for methods of adding directories containing packages after GAP has been started.

76.2-3 SetPackagePath
‣ SetPackagePath( pkgname, pkgpath )( function )

This function can be used to force GAP to load a particular version of a package, even though newer versions of the package are available.

Let pkgname and pkgpath be strings denoting the name of a GAP package and the path to a directory where a version of this package can be found (i. e., calling Directory (9.4-2) with the argument pkgpath will yield a directory that contains the file PackageInfo.g of the package).

If the package pkgname is already loaded with an installation path different from pkgpath then SetPackagePath signals an error. If the package pkgname is not yet loaded then SetPackagePath erases the information about available versions of the package pkgname, and stores the record that is contained in the PackageInfo.g file at pkgpath instead, such that only the version installed at pkgpath can be loaded with LoadPackage (76.2-1).

One should call SetPackagePath immediately before loading the package in question. Note that calling ExtendPackageDirectories (76.2-5) or ExtendRootDirectories (76.2-4) may change the available versions of the package pkgname.

76.2-4 ExtendRootDirectories
‣ ExtendRootDirectories( paths )( function )

Let paths be a list of strings that denote paths to intended GAP root directories (see 9.2). The function ExtendRootDirectories adds these paths to the global list GAPInfo.RootPaths and calls the initialization of available GAP packages, such that later calls to LoadPackage (76.2-1) will find the GAP packages that are contained in pkg subdirectories of the directories given by paths.

Note that the purpose of this function is to make GAP packages in the given directories available. It cannot be used to influence the start of GAP, because the GAP library is loaded before ExtendRootDirectories can be called (and because GAPInfo.RootPaths is not used for reading the GAP library).

76.2-5 ExtendPackageDirectories
‣ ExtendPackageDirectories( paths )( function )

Let paths be a list of strings that denote paths to intended GAP package directories (see 9.3). The function ExtendPackageDirectories adds these paths to the global list GAPInfo.PackageDirectories and calls the initialization of available GAP packages, such that later calls to LoadPackage (76.2-1) will find the GAP packages that are contained in the directories given by paths.

76.2-6 DisplayPackageLoadingLog
‣ DisplayPackageLoadingLog( [severity] )( function )
‣ InfoPackageLoading( info class )
‣ PACKAGE_ERROR( global variable )
‣ PACKAGE_WARNING( global variable )
‣ PACKAGE_INFO( global variable )
‣ PACKAGE_DEBUG( global variable )
‣ LogPackageLoadingMessage( severity, message[, name] )( function )

Whenever GAP considers loading a package, log messages are collected in a global list. The messages for the current GAP session can be displayed with DisplayPackageLoadingLog. To each message, a severity is assigned, which is one of PACKAGE_ERROR, PACKAGE_WARNING, PACKAGE_INFO, PACKAGE_DEBUG, in increasing order. The function DisplayPackageLoadingLog shows only the messages whose severity is at most severity, the default for severity is PACKAGE_WARNING.

The intended meaning of the severity levels is as follows.

PACKAGE_ERROR

should be used whenever GAP will run into an error during package loading, where the reason of the error shall be documented in the global list.

PACKAGE_WARNING

should be used whenever GAP has detected a reason why a package cannot be loaded, and where the message describes how to solve this problem, for example if a package binary is missing.

PACKAGE_INFO

should be used whenever GAP has detected a reason why a package cannot be loaded, and where it is not clear how to solve this problem, for example if the package is not compatible with other installed packages.

PACKAGE_DEBUG

should be used for other messages reporting what GAP does when it loads packages (checking dependencies, reading files, etc.). One purpose is to record in which order packages have been considered for loading or have actually been loaded.

The log messages are created either by the functions of GAP's package loading mechanism or in the code of your package, for example in the AvailabilityTest function of the package's PackageInfo.g file (see 76.5), using LogPackageLoadingMessage. The arguments of this function are severity (which must be one of the above severity levels), message (which must be either a string or a list of strings), and optionally name (which must be the name of the package to which the message belongs). The argument name is not needed if the function is called from a call of a package's AvailabilityTest function (see 76.5) or is called from a package file that is read from init.g or read.g; in these cases, the name of the current package (stored in the record GAPInfo.PackageCurrent) is taken. According to the above list, the severity argument of LogPackageLoadingMessage calls in a package's AvailabilityTest function is either PACKAGE_WARNING or PACKAGE_INFO.

If you want to see the log messages already during the package loading process, you can set the level of the info class InfoPackageLoading to one of the severity values listed above; afterwards the messages with at most this severity are shown immediately when they arise. In order to make this work already for autoloaded packages, you can call SetUserPreference("InfoPackageLoadingLevel", lev); to set the desired severity level lev. This can for example be done in your gap.ini file, see Section 3.2-1.

76.2-7 TestPackageAvailability
‣ TestPackageAvailability( name[, version][, checkall] )( function )

For strings name and version, this function tests whether the GAP package name is available for loading in a version that is at least version, or equal to version if the first character of version is = (see CompareVersionNumbers (76.5-3) for further details about version numbers).

The result is true if the package is already loaded, fail if it is not available, and the string denoting the GAP root path where the package resides if it is available, but not yet loaded. So the package name is available if the result of TestPackageAvailability is not equal to fail.

If the optional argument checkall is true then all dependencies are checked, even if some have turned out to be not satisfied. This is useful when one is interested in the reasons why the package name cannot be loaded. In this situation, calling first TestPackageAvailability and then DisplayPackageLoadingLog (76.2-6) with argument PACKAGE_INFO (76.2-6) will give an overview of these reasons.

You should not call TestPackageAvailability in the test function of a package (the value of the component AvailabilityTest in the PackageInfo.g file of the package, see 76.5), because TestPackageAvailability calls this test function.

The argument name is case insensitive.

76.2-8 IsPackageLoaded
‣ IsPackageLoaded( name[, version] )( function )

For strings name and version, this function tests whether the GAP package name is already loaded in a version that is at least version, or equal to version if the first character of version is = (see CompareVersionNumbers (76.5-3) for further details about version numbers).

The result is true if the package is already loaded, false otherwise.

76.2-9 InstalledPackageVersion
‣ InstalledPackageVersion( name )( function )

If the GAP package with name name has already been loaded then InstalledPackageVersion returns the string denoting the version number of this version of the package. If the package is available but has not yet been loaded then the version number string for that version of the package that currently would be loaded. (Note that loading another package might force loading another version of the package name, so the result of InstalledPackageVersion will be different afterwards.) If the package is not available then fail is returned.

The argument name is case insensitive.

76.3 Citing a GAP Package

If a package contributed to your work, please cite it, in addition to GAP itself. Cite (76.3-1) prints a suggested citation, computed from the PackageInfo.g file of the package, and BibEntry (76.3-2) returns it as an entry in BibXMLext format.

76.3-1 Cite
‣ Cite( [pkgname[, key]] )( function )

Used with no arguments or with argument "GAP" (case-insensitive), Cite displays instructions on citing the version of GAP that is being used. Suggestions are given in plain text, HTML, BibXML and BibTeX formats. The same instructions are also contained in the CITATION file in the GAP root directory.

If pkgname is the name of a GAP package, instructions on citing this package will be displayed. They will be produced from the PackageInfo.g file of the working version of this package that must be available in the GAP installation being used. Otherwise, one will get a warning that no working version of the package is available.

The optional 2nd argument key has the same meaning as in BibEntry (76.3-2).

76.3-2 BibEntry
‣ BibEntry( pkgname[, key] )( function )

Returns: a string in BibXMLext format (see GAPDoc: The BibXMLext Format) that can be used for referencing the GAP system or a GAP package.

If the argument pkgname is the string "GAP", the function returns an entry for the current version of GAP.

Otherwise, if a string pkgname is given, which is the name of a GAP package, an entry for this package is returned; this entry is computed from the PackageInfo.g file of the current version of the package, see InstalledPackageVersion (76.2-9). If no package with name pkgname is installed then the empty string is returned.

A string for a different version of GAP or a package can be computed by entering, as the argument pkgname, the desired record from the PackageInfo.g file. (One can access these records using the function PackageInfo.)

In each of the above cases, an optional argument key can be given, a string which is then used as the key of the BibTeX entry instead of the default key that is generated from the system/package name and the version number.

BibEntry requires the functions FormatParagraph (GAPDoc: FormatParagraph) and NormalizedNameAndKey (GAPDoc: NormalizedNameAndKey) from the GAP package GAPDoc.

The functions ParseBibXMLextString (GAPDoc: ParseBibXMLextString) and StringBibXMLEntry (GAPDoc: StringBibXMLEntry) can be used to create for example a BibTeX entry from the return value, as follows.

gap> bib:= BibEntry( "GAP", "GAP4.5" );;
gap> Print( bib, "\n" );
<entry id="GAP4.5"><misc>
  <title><C>GAP</C> &ndash; <C>G</C>roups, <C>A</C>lgorithms,
         and <C>P</C>rogramming, <C>V</C>ersion 4.5.1</title>
  <howpublished><URL>https://www.gap-system.org</URL></howpublished>
  <key>GAP</key>
  <keywords>groups; *; gap; manual</keywords>
  <other type="organization">The GAP <C>G</C>roup</other>
</misc></entry>
gap> parse:= ParseBibXMLextString( bib );;
gap> Print( StringBibXMLEntry( parse.entries[1], "BibTeX" ) );
@misc{ GAP4.5,
  title =            {{GAP}   {\textendash}   {G}roups,   {A}lgorithms,  and
                      {P}rogramming, {V}ersion 4.5.1},
  organization =     {The GAP {G}roup},
  howpublished =     {\href                      {https://www.gap-system.org}
                      {\texttt{https://www.gap-system.org}}},
  key =              {GAP},
  keywords =         {groups; *; gap; manual}
}

76.4 Structure of a GAP Package

A GAP package should have an alphanumeric name; mixed case is fine, but there should be no whitespace characters. All files of a GAP package packagename must be collected in a single directory packagedir, where packagedir should be just packagename optionally converted to lowercase and optionally followed by the package version (with or without hyphen to separate the version from packagename). Let us call this directory the home directory of the package.

To use the package with GAP, the directory packagedir must be a subdirectory of a pkg directory in (one of) the GAP root directories (see 9.2). For example, if GAP is installed in /usr/local/gap4 then the files of the package MyPack may be placed in the directory /usr/local/gap4/pkg/mypack. The directory packagedir preferably should have the following structure (below, a trailing / distinguishes directories from ordinary files):

packagedir/
  doc/
  lib/
  tst/
  CHANGES
  LICENSE
  README
  PackageInfo.g
  init.g
  read.g

This layout of directories and files may be created manually, or automatically with the PackageMaker package, which is distributed with GAP (see PackageMaker: PackageMaker). Its function PackageWizard asks several questions about the intended package and then creates a new directory for it in the current directory and populates it with all the files needed for a basic package.

gap> LoadPackage("packagemaker");
true
gap> PackageWizard();
Welcome to the GAP PackageMaker Wizard.
I will now guide you step-by-step through the package
creation process by asking you some questions.

What is the name of the package? mypkg
Enter a short (one sentence) description of your package:

Packages that contain some code that requires compilation will usually have it in the src subdirectory. They may also have extra files such as configure, Makefile.in etc. that automate the build procedure (see Sections 76.10 and 76.11).

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.

On the other hand, the names of CHANGES, LICENSE and README files are not strictly fixed. They may have extensions .txt or .md, and instead of LICENSE one could use e.g. COPYING or GPL for packages distributed under the GNU General Public License, or use HISTORY instead of CHANGES.

We now describe the above files and directories in more details:

README

The filename may optionally have an extension, e.g. .txt or .md.

This should contain how to get it instructions (covering the way of getting it with the GAP distribution and from the GAP website, if applicable), as well as installation instructions and names of the package authors and their email addresses. These installation instructions should be repeated or referenced from the package's documentation, which should be in the doc directory. Authors' names and addresses should be repeated both in the package's documentation and in the PackageInfo.g (see below).

CHANGES

For further versions of the package, it will be also useful to have a CHANGES file that records the main changes between versions of the package.

The filename may optionally have an extension, e.g. .txt or .md.

LICENSE

The file which explains conditions on which the package is distributed.

The filename may optionally have an extension, e.g. .txt or .md. Some packages also use different filenames, like COPYING.

configure, Makefile.in

These files are typically only used by packages which have a non-GAP component, e.g. some C code (the files of which should be in the src directory). The configure and Makefile.in files of the Example package provide prototypes (or they may be created using the PackageMaker mentioned above). The configure file typically takes a path path to the GAP root directory as argument and uses the value assigned to GAParch in the file sysinfo.gap, created when GAP was compiled to determine the compilation architecture, inserts this in place of the string @GAPARCH@ in Makefile.in and creates a file Makefile. When make is run (which, of course, reads the constructed Makefile), a directory bin (if necessary) and subdirectories of bin with the path equal to the string assigned to GAParch in the file sysinfo.gap should be created; any binaries constructed by compiling the code in src should end up in this subdirectory of bin.

PackageInfo.g

Every GAP package must have a PackageInfo.g file which contains meta-information about the package (package name, version, author(s), relations to other packages, homepage, download archives, etc.). This information is used by the package loading mechanism and also for the redistribution of a package with GAP. Its contents are described in Section 76.5. The Example package's PackageInfo.g file is well-commented and can be used as a prototype. It may also be created using the PackageMaker mentioned above.

init.g, read.g

A GAP package must have a file init.g; the file read.g is optional. Both should normally consist entirely of ReadPackage (76.4-1) commands (and possibly also Read (9.8-1) commands) for reading further files of the package, which should go in the package's lib directory (see below).

It is recommended to separate the declaration part of a package, the files with .gd extension that declare the names of its functions and variables, from the implementation part, the files with .gi extension that define them. Then init.g reads the former and read.g reads the latter; see Section 76.9 for the reason.

doc

This directory should contain the package's documentation, written in an XML-based documentation format supported by the GAP package GAPDoc (see GAPDoc: Introduction and Example) which is used for the GAP documentation itself.

The Example package's documentation (see its doc directory) may be used as a prototype. It consists of the master file main.xml, further .xml files for manual chapters (included in the manual via Include directives in the master file) and the GAP input file ../makedocrel.g which generates the manuals. Generally, one should also provide a manual.bib BibTeX database file or an xml file in the BibXMLext format (see GAPDoc: The BibXMLext Format).

One could also use the AutoDoc which simplifies writing documentation by generating most of the GAPDoc code automatically.

lib

This is the preferred place for the GAP code of the package, i.e. the .g, .gd and .gi files (other than PackageInfo.g, init.g and read.g). For some packages, the directory gap has been used instead of lib; lib has the advantage that it is the default subdirectory of a package directory searched for by the DirectoriesPackageLibrary (76.4-2) command.

src

If the package contains non-GAP code, e.g. C code, then this source code should go in the src directory. If there are .h include files you may prefer to put these all together in a separate include directory. There is one further rule for the location of kernel library modules or external programs which is explained in 76.10-1.

tst

It is highly recommended that a package should have test files, which then should go in the tst directory. A test file with a basic test of the package (for example, to check that it works as expected and/or that the manual examples are correct) should be specified by the component TestFile in PackageInfo.g. More specific and time consuming tests are not supposed to be a part of the GAP standard test suite but may be placed in the tst directory with further instructions on how to run them. See Section 76.12 for the requirements on test files.

All other files can be organised as you like. But we suggest that you have a look at existing packages and use a similar scheme, for example, put examples in the examples subdirectory, data libraries in extra subdirectories, and so on.

Sometimes there may be a need to include an empty directory in the package distribution (for example, as a place to store some data that may appear at runtime). In this case package authors are advised to put in this directory a short README file describing its purpose to ensure that such directory will be included in the redistribution.

Concerning the GAP code in packages, it is recommended to use only documented GAP functions, see 83.3. In particular if you want to make your package available to other GAP users it is advisable to avoid using obsolete variables (see 77). To test that the package does not use obsolete variables you can set the ReadObsolete component in your gap.ini file to false (see 3.2) or start GAP with -A -O command line options (note that this may also cause problems with loading other packages that use obsolete variables).

The files of a package are read with ReadPackage (76.4-1), and its directories are located with DirectoriesPackageLibrary (76.4-2).

76.4-1 ReadPackage
‣ ReadPackage( [name, ]file )( function )
‣ RereadPackage( [name, ]file )( function )

Called with two strings name and file, ReadPackage reads the file file of the GAP package name, where file is given as a path relative to the home directory of name. Note that file is read in the namespace of the package, see Section 4.10 for details.

If only one argument file is given, this should be the path of a file relative to the pkg subdirectory of GAP root paths (see 9.2). Note that in this case, the package name is assumed to be equal to the first part of file, so the one argument form is not recommended.

The absolute path is determined as follows. If the package in question has already been loaded then the file in the directory of the loaded version is read. If the package is available but not yet loaded then the directory given by TestPackageAvailability (76.2-7) is used, without prescribed version number. (Note that the ReadPackage call does not force the package to be loaded.)

If the file is readable then true is returned, otherwise a warning is displayed (for ReadPackage) or false is returned (for RereadPackage).

Each of name and file should be a string. The name argument is case insensitive.

RereadPackage does the same as ReadPackage, except that also read-only global variables are overwritten (cf. Reread (9.8-11)).

76.4-2 DirectoriesPackageLibrary
‣ DirectoriesPackageLibrary( name[, path] )( function )

takes the string name, a name of a GAP package, and returns a list that is either empty or contains one directory object dir that describes the place where the library functions of this GAP package should be located.

In the latter case, dir is the path subdirectory of a directory where the package name is installed, where the default for path is "lib", and where the package directory belongs to the version of name that is already loaded or is currently going to be loaded or would be the first version GAP would try to load if no other version is explicitly prescribed. (If the package name is not yet loaded then we cannot guarantee that the directory belongs to a version that really can be loaded.)

Note that DirectoriesPackageLibrary is likely to be called in the AvailabilityTest function in the package's PackageInfo.g file (see 76.5).

As an example, the following returns a directory object for the library functions of the GAP package Example:

gap> DirectoriesPackageLibrary( "Example", "gap" );
[ dir("/home/werner/gap/4.0/pkg/example/gap/") ]

Observe that we needed the second argument "gap" here, since Example's library functions are in the subdirectory gap rather than lib.

In order to find a subdirectory deeper than one level in a package directory, the second argument is again necessary whether or not the desired subdirectory relative to the package's directory begins with lib. The directories in path should be separated by / (even on systems, like Windows, which use \ as the directory separator). For example, suppose there is a package somepackage with a subdirectory m11 in the directory data, then we might expect the following:

gap> DirectoriesPackageLibrary( "somepackage", "data/m11" );
[ dir("/home/werner/gap/4.0/pkg/somepackage/data/m11") ]

76.5 The PackageInfo.g File

Each package has the file PackageInfo.g which contains meta-information about the package (package name, version, author(s), relations to other packages, homepage, download archives, etc.). This file is used by the package loading mechanism, by the GAP webpages about packages, and also for the redistribution of a package with GAP.

A PackageInfo.g file contains a call to the function SetPackageInfo, with argument a record. The following components of this record are mandatory.

PackageName

a nonempty string denoting the name of the package,

Subtitle

a string that describes the package's contents, may be used by a default banner or on a web page, should fit on one line,

Version

a nonempty string that does not start with =, denoting the version number of the package (see 76.5-2),

Date

a string of the form yyyy-mm-dd denoting the release date of the current version of the package (a date since 1999, when GAP 4 appeared),

License

a nonempty string containing the SPDX identifier of the license of the package (see https://spdx.org/licenses/), for example "GPL-2.0-or-later",

ArchiveURL

a string started with http://, https://, or ftp://, denoting an URL from where the current package archive can be downloaded, but without the suffix describing the format (see the ArchiveFormats component),

ArchiveFormats

a string that lists the supported formats (among .tar.gz, .tar.bz2, -win.zip), separated by whitespace or commas,

README_URL

a string started with http://, https://, or ftp://, denoting an URL from where the current README.md or README file of the package can be downloaded,

PackageInfoURL

a string started with http://, https://, or ftp://, denoting an URL from where the current PackageInfo.g file of the package can be downloaded,

AbstractHTML

a string that describes the package's contents in a few lines, in HTML format; this text will be displayed on the package overview web page of GAP,

PackageWWWHome

a string started with http://, https://, or ftp://, denoting the address of the package's home page,

PackageDoc

a record or a list of records; each record describes a book of the package documentation, with the following components

BookName

a string, the name of the book,

LongTitle

a string shown by ?books,

SixFile

a string denoting a relative path to the manual.six file of the book,

HTMLStart

a string denoting a relative path to the start file of the HTML version of the book,

PDFFile

a string denoting a relative path to the .pdf file of the book,

ArchiveURLSubset

a list of strings denoting relative paths to those files and directories from the archive that are needed for the online manual; typically, [ "doc" ] suffices,

The following components of the record are optional.

TextFiles or BinaryFiles or TextBinaryFilesPatterns

a list of strings that specify which files in the archive are text files or binary files (at most one of the three components can be available, each string in TextBinaryFilesPatterns must start with T for text files and by B for binary files),

Persons

a list of records, each with the mandatory components

LastName

a string,

at least one of IsAuthor or IsMaintainer

true or false,

and optional components

FirstNames

a string (was mandatory before GAP 4.14),

Place

a string,

Institution

a string,

GitHubUsername

a string containing a GitHub username.

If the IsMaintainer value is true then also one of the following components is mandatory, otherwise these components are optional.

Email

a string,

WWWHome

a string denoting an URL, or

PostalAddress

a string.

SourceRepository

a record with the components Type (the version control system, e.g. "git" or "hg") and URL (the URL of the repository), both strings,

IssueTrackerURL

a string started with http://, https://, or ftp://,

SupportEmail

a string denoting an e-mail address,

Dependencies

a record describing the dependencies of the package (see Section 76.6), with the following optional components

GAP

a string denoting the needed version of GAP,

NeededOtherPackages

a list of pairs [ pkgname, pkgversion ] of strings, denoting the other packages which must be available if the current package shall be loadable,

SuggestedOtherPackages

a list of pairs [ pkgname, pkgversion ] of strings, denoting the other packages which shall be loaded together with the current package if they are available,

TestPackages

a list of pairs [ pkgname, pkgversion ] of strings, denoting packages that are needed only to test the package, but not to load the package,

NeededSystemPackages

a record whose contents describe external dependencies of the package in a machine-readable form (see 76.7 for more information),

ExternalConditions

a list of strings or of pairs [ text, URL ] of strings, denoting conditions on external programs in a human-readable form (see 76.7 for more information),

AvailabilityTest

a function with no arguments that returns true if the package is available, and false otherwise (can be ReturnTrue (5.4-1) if the package consists only of GAP code; this is also the default value; see 76.10-3 and Section 76.11 for examples),

BannerString or BannerFunction

a string or a function, respectively, that is used to create a package banner different from the default banner (see 76.5-4),

TestFile

a string denoting a relative path to a readable file which contains tests of the package's functionality (see Section 76.12),

Keywords

a list of strings that are keywords related to the topic of the package,

Extensions

a list of records that describe conditional extensions of the package (see Section 76.8).

Other components of the record can be supported; for example, AutoDoc is used by the AutoDoc package if applicable.

76.5-1 ValidatePackageInfo
‣ ValidatePackageInfo( info )( function )

This function is intended to support package authors who create or modify PackageInfo.g files. (It is not called when these files are read during the startup of GAP or when packages are actually loaded.)

The argument info must be either a record as is contained in a PackageInfo.g file or a string which describes the path to such a file. The result is true if the record or the contents of the file, respectively, has correct format, and false otherwise; in the latter case information about the incorrect components is printed. These diagnostic messages can be suppressed by setting the global option quiet to true.

Note that the components used for package loading are checked as well as the components that are needed for composing the package overview web page or for updating the package archives.

If info is a string then ValidatePackageInfo checks additionally whether those package files exist that are mentioned in the file info, for example the manual.six file of the package documentation.

76.5-2 Version Numbers

Version numbers are strings containing nonnegative integers separated by non-numeric characters. They are compared by CompareVersionNumbers (76.5-3) which first splits them at non-digit characters and then lexicographically compares the resulting integer lists. Thus version "2-3" is larger than version "2-2-5" but smaller than "4r2p3" or "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.

Loading a specific version of a package (that is, not one with a larger version number) can be achieved by prepending = to the desired version number. For example, LoadPackage( "example", "=1.0" ) will load version "1.0" of the package "example", even if version "1.1" is available. As a consequence, version numbers must not start with =, so "=1.0" is not a valid version number.

Package authors should choose a version numbering scheme that admits a new version number even after tiny changes to the package, and ensure that version numbers of successive package versions increase. The automatic update of package archives in the GAP distribution will only work if a package has a new version number.

76.5-3 CompareVersionNumbers
‣ CompareVersionNumbers( supplied, required[, "equal"] )( function )

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"

CompareVersionNumbers compares two version numbers, given as strings. They are split at non-digit characters, the resulting integer lists are compared lexicographically. The routine tests whether supplied is at least as large as required, and returns true or false accordingly. A version number ending in dev is considered to be infinite.

76.5-4 The Banner

When the package is loaded, GAP will display a default package banner, constructed from the package metadata provided in the PackageInfo.g file.

Alternatively, the package may establish its own banner by assigning either a string to the BannerString field of the record argument of SetPackageInfo in the PackageInfo.g file or a function to the BannerFunction field, which takes this record as its unique argument. The latter possibility can be useful if the banner shall show information that is available only at runtime.

If you will be designing a banner for your package, it is a good idea to suggest there how to access package documentation. For example, the banner of the Example package says:

For help, type: ?Example package

In order for this to display the introduction of the Example package the index-entry <Index>Example package</Index> was added just before the first paragraph of the introductory section in the file doc/example.xml of the Example package.

76.6 Package Dependencies (Requesting one GAP Package from within Another)

It is possible for one GAP package A 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 Dependencies 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.

If B is not essential for A but is used in the package tests, (for example because B is a library of groups used to provide testcases) then the name and the (least) version number of B should be added to the TestPackages component of the Dependencies component of the PackageInfo.g file of A. Note that GAP itself does not use this information at all. In particular, no attempts are made to actually load B, this should be done explicitly as part of the package tests. The benefit of specifying this component is for automated test runners (e.g. as part of the GAP package distribution) which can use this information to ensure all packages needed to run your package's tests are installed and ready.

All package dependencies must be documented explicitly in the PackageInfo.g file. It is important to properly identify package dependencies and make the right decision whether the other package should be needed or suggested. For example, declaring package as needed when suggested might be sufficient may prevent loading of packages under Windows for no good reason.

It is not appropriate to explicitly call LoadPackage (76.2-1) when the package is loaded, since this may distort the order of package loading and result in warning messages. It is recommended to turn such dependencies into needed or suggested packages. For example, a package can be designed in such a way that it can be loaded with restricted functionality if another package (or standalone program) is missing, and in this case the missing package (or binary) is suggested. Alternatively, if the package author decides that loading the package in this situation makes no sense, then the missing component is needed.

Do not call LoadPackage (76.2-1) inside functions of the package either. Loading a package can install new methods and thus change which methods get selected, so objects created before the call may then behave differently from those created after it. Declare the other package as needed or suggested instead, and use IsPackageMarkedForLoading (76.6-1) to check whether a suggested package is available. Test files and manual examples may call LoadPackage (76.2-1) at their start.

It may happen that a package B that is listed as a suggested package of package A is actually needed by A. If no explicit LoadPackage (76.2-1) calls for B occur in A at loading time, this can now be detected using the new possibility to load a package without loading its suggested packages using the global option OnlyNeeded which can be used to (recursively) suppress loading the suggested packages of the package in question. Using this option, one can check whether errors or warnings appear when B is not available (note that this option should be used only for such checks to simulate the situation when package B is not available; it is not supposed to be used in an actual GAP session when package B will be loaded later, since this may cause problems). In case of any errors or warnings, their consequence can then be either turning B into a needed package or (since apparently B was not intended to become a needed package) changing the code accordingly. Only if package A calls LoadPackage (76.2-1) for B at loading time (see above) then package B needs to be deinstalled (i.e. removed) to test loading of A without B.

76.6-1 IsPackageMarkedForLoading
‣ IsPackageMarkedForLoading( name, version )( function )

This function can be used in the code of a package A for testing whether the package name in version version will be loaded after the LoadPackage (76.2-1) call for the package A has been executed. This means that the package name had been loaded before, or has been (directly or indirectly) requested as a needed or suggested package of the package A or of a package whose loading requested that A was loaded.

76.7 External Dependencies (System packages needed by a GAP Package)

It is possible for a GAP package to require external software. If this software is not bundled together with the package, then this dependency should be documented. To do so, one may add either a string describing the dependency, or a list [ description, URL ], to the ExternalConditions component of the Dependencies component of the PackageInfo.g file of the GAP package.

ExternalConditions := [
    [ "needs the PARI/GP computer algebra system Version 2.5 or higher",
      "https://pari.math.u-bordeaux.fr/" ]
]

Note that the data in ExternalConditions is purely informational and meant to be human-readable, not machine-readable. GAP itself does not use it, but for packages distributed with GAP, the GAP website displays it in its package list.

If this dependency is available through a package manager such as apt or brew, this should also be documented. This can be done by adding the system package to the relevant component (such as Ubuntu or Homebrew) of the NeededSystemPackages component of the PackageInfo.g file of the GAP package. Each system package is added by means of a list whose first component is the name needed by the package manager. The other elements of this list are reserved for future usage.

NeededSystemPackages := rec(
    Ubuntu   := [["pari-gp"]],
    Homebrew := [["pari"]]
)

Note that this mechanism is primarily intended for consumption by the GAP package distribution and by other testing tooling. It is deliberately kept very simple. For example, at this point there is no mechanism to prescribe a distribution version (basically, "Ubuntu" means whatever `ubuntu-latest` is referring to in GitHub actions), nor to specify a minimal version of a system package. Despite these strong limitations, this data may still be useful for people packaging GAP and its packages for a distribution, as it at least gives a hint which system packages may be needed.

76.8 Extensions Provided by a Package

Sometimes a package A can provide additional functionality, such as better methods or additional data, if some other packages B, C, etc. are loaded. However, one would like package A to still be usable without these additional packages, and therefore B, C, etc. shall not be regarded as needed packages (see Section 76.6) of A.

One way to deal with this situation is to put those parts of code of A that depend on B, C, etc., into files that get read only in the situation that the packages in question have actually been loaded into the current GAP session.

However, this leaves the question when to load these files of a conditional extension of A. In the past, the only option for A was to check for the presence of B, C, etc., while it itself was being loaded. With this setup, it depends on the order in which packages get loaded whether some feature is available or not: If B is loaded before A, the extension might be loaded as well; if B is loaded only after A, then the extension is not loaded.

To deal with this issue of conditional extensions of packages, GAP offers a dedicated mechanism: The Extensions component of the PackageInfo.g file of A is a list of declarations of conditional extension of A, each being a record with the following components.

needed

a list of the form [ [ pkgname1, version1 ], [ pkgname2, version2 ], ... ], meaning that the extension shall be loaded as soon as all packages pkgname1, pkgname2, ..., with versions (at least) version1, version2, ..., have been loaded,

filename (optional)

the path, relative to the package directory of A, of a file such that reading this file will load the code of the extension,

testfiles (optional)

a list of paths, relative to the package directory of A, of test files and directories that TestDirectory (7.10-3) skips as long as the extension is not loaded.

GAP ignores all other components, so a package can use components introduced by later GAP versions. GAP versions before 4.17 ignore testfiles and cannot load packages with extensions without filename; a package using either should therefore need GAP 4.17 or newer.

As an example suppose the following is part of the PackageInfo.g. Then GAP will load the file fileForB.gd as soon as package B is loaded in version 0.6 or newer, and fileForCD.gi once package C and D are loaded in version 1.2 and 0.1 or newer respectively. TestDirectory (7.10-3) runs tst/B.tst only if B is loaded, and the test files in tst/BC only if B and C are loaded.

Extensions := [
  rec(
    needed := [ ["B", "0.6"] ],
    filename := "gap/fileForB.gd",
    testfiles := [ "tst/B.tst" ],
  ),
  rec(
    needed := [ ["C", "1.2"] , ["D", "0.1"] ],
    filename := "gap/fileForCD.gi",
  ),
  rec(
    needed := [ ["B", "0.6"] , ["C", "1.2"] ],
    testfiles := [ "tst/BC" ],
  ),
],

Whenever LoadPackage (76.2-1) is called, GAP checks for package extensions whose conditions now are satisfied, and loads them.

For example, package A can be loaded early in a GAP session, and declare in its PackageInfo.g the availability of an extension that requires package B. If B has not yet been loaded then this extension will not be loaded together with A. However, as soon as B gets (installed and) loaded later in the session, also the extension of A will automatically get loaded.

The contents of Extensions in a PackageInfo.g file does not affect the lists of needed or suggested packages. If an extension of A is beneficial for the functions of A then it makes sense to list the packages needed for the extension among the suggested packages of A, but this may not be the case if the extension is beneficial only for the functions of its needed packages.

76.9 Declaration and Implementation Part of a Package

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, and 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 is done for the GAP library, by creating a file read.g, restricting the ReadPackage (76.4-1) statements in init.g to only read those files of the package that provide declarations, and to read the implementation files from read.g.

Examples:

Suppose that there are two packages A and B, each with files init.g and read.g.

In general, when GAP is asked to load a package then first the dependencies between this packages and its needed and suggested packages are inspected (recursively), and a list of package sets is computed such that no cyclic dependencies occur between different package sets and such that no package in any of the package sets needs any package in later package sets. Then GAP runs through the package sets and reads for each set first all init.g files and then all read.g files of the packages in the set. (There is one exception from this rule: Whenever packages are autoloaded before the implementation part of the GAP library is read, only the init.g files of the packages are read; as soon as the GAP library has been read, the read.g files of these packages are also read, and afterwards the above rule holds.)

It can happen that some code of a package depends on the availability of suggested packages, i.e., different initialisations are performed depending on whether a suggested package will eventually be loaded or not. One can test this condition with the function IsPackageMarkedForLoading (76.6-1). In particular, one should not call (and use the value returned by this call) the function LoadPackage (76.2-1) inside package code that is read during package loading. Note that for debugging purposes loading suggested packages may have been deliberately disabled via the global option OnlyNeeded.

Note that the separation of the GAP code of packages into declaration part and implementation part does in general not allow one to actually call functions from a package when the implementation part is read. For example, in the case of a cyclic dependency as in the second example above, suppose that B provides a new function f or a new global record r which are declared in the declaration part of B. Then the code in the implementation part of A may contain calls to the functions defined in the declaration part of B. However, the implementation part of A may be read before the implementation part of B. So one can in general not assume that during the loading of A, the function f can be called, or that one can access components of the record r.

If one wants to call the function f or to access components of the record r in the code of the package A then the problem is that it may be not possible to determine a cyclic dependency between A and B from the packages A and B alone. A safe solution is then to design A in such a way that the code that calls f or accesses r belongs to package extensions of A that get loaded only after B has been loaded; see Section 76.8 for details.

In the case of cyclic dependencies, one solution for the above problem might be to delay those computations (typically initialisations) in package A that require package B to be loaded until all required packages are completely loaded. This can be done by moving the declaration and implementation of the variables that are created in the initialisation into a separate file and to declare these variables in the init.g file of the package, via a call to DeclareAutoreadableVariables (76.9-2) (see 76.9-1).

76.9-1 Autoreadable Variables

Package files containing method installations must be read when the package is loaded. For package files not containing method installations (this applies, for example, to many data files) DeclareAutoreadableVariables (76.9-2) allows one to delay reading such files until the data are actually accessed.

76.9-2 DeclareAutoreadableVariables
‣ DeclareAutoreadableVariables( pkgname, filename, varlist )( function )

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.

76.10 Standalone Programs in a GAP 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 cover 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 recommend the provision of 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 76.6) being the actual GAP package.

Instead of calling an external binary, a package can also extend the GAP kernel itself, see Section 76.11.

76.10-1 Installation of GAP Package Binaries

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). As initial templates, you may use installation scripts of the Example package or files generated with the help of PackageMaker.

76.10-2 DirectoriesPackagePrograms
‣ DirectoriesPackagePrograms( name )( function )

returns a list that is either empty or contains one directory object dir that describes the place where external binaries of the GAP package name should be located.

In the latter case, dir is the bin/architecture subdirectory of a directory where the package name is installed, where architecture is the architecture on which GAP has been compiled (this can be accessed as GAPInfo.Architecture, see GAPInfo (3.5-1)), and where the package directory belongs to the version of name that is already loaded or is currently going to be loaded or would be the first version GAP would try to load if no other version is explicitly prescribed. (If the package name is not yet loaded then we cannot guarantee that the directory belongs to a version that really can be loaded.)

Note that DirectoriesPackagePrograms is likely to be called in the AvailabilityTest function in the package's PackageInfo.g file (see 76.5).

gap> DirectoriesPackagePrograms( "nq" );
[ dir("/home/gap/4.0/pkg/nq/bin/x86_64-pc-linux-gnu-default64-kv3/") ]

76.10-3 Test for the Existence of GAP Package Binaries

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 (9.5-1) for checking for the actual binaries in the path given by DirectoriesPackagePrograms (76.10-2) for the respective package. For example the example GAP package could use the following function to test whether the binary hello has been compiled; it will issue a warning if not, and will only load the package if the binary 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
      LogPackageLoadingMessage( PACKAGE_WARNING,
          [ "The program `hello' is not compiled,",
            "`HelloWorld()' is thus unavailable.",
            "See the installation instructions;",
            "type: ?Installing the Example package" ] );
    fi;
    return file <> fail;
  end,
...

However, if you look at the actual PackageInfo.g file of the example package, you will see that its AvailabilityTest function always returns true, and just logs the warning if the binary is not available (which may be later viewed with DisplayPackageLoadingLog (76.2-6)). 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 e.g. under Windows), or may not compile with some compilers or default compiler options. See 3.4 for information on how to test for the architecture.

Last but not least: do not print anything in the AvailabilityTest function of the package via Print or Info. Instead one should call LogPackageLoadingMessage (76.2-6) to store a message which may be viewed later with DisplayPackageLoadingLog (76.2-6) (the latter two functions have been introduced in GAP 4.5)

76.10-4 Calling of and Communication with External Binaries

The GAP code of the package has to start the stand-alone program and to pass the input data on to it. 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 (11.1-1) or Exec (11.1-2) which then writes the output data to file, and finally read in the standalone's output file.

The second way is interfacing via input-output streams, see Section 10.8.

76.11 Kernel Modules in GAP Packages

Some GAP packages use a kernel module instead of an external binary. A kernel module is implemented in C and follows certain conventions to comply with the GAP kernel interface, which we plan to document later. In the meantime, we advise you to look at existing examples of such packages and to get in touch with GAP developers if you plan to develop such a package.

A kernel module can be compiled using the gac script. To produce a dynamically loadable module, call it with the -d option, for example:

$ gap4/gac -d test.c

This will produce a file test.so, which then can be loaded into GAP with LoadKernelExtension (76.11-2).

Note that before GAP 4.12, LoadDynamicModule (76.11-3) was used for this. It is still available and in fact LoadKernelExtension (76.11-2) calls it; but the latter provides a higher level abstraction and is more convenient to use.

If the kernel module is required for the package to work, then the AvailabilityTest function in its PackageInfo.g file should call IsKernelExtensionAvailable (76.11-1), like this (see 76.10-3 for the general rules):

...
AvailabilityTest := function()
    # see if example.so exists and is a loadable kernel extension
    if not IsKernelExtensionAvailable("example") then
      LogPackageLoadingMessage( PACKAGE_WARNING,
          [ "The kernel extension `example' is unavailable,",
            "perhaps it needs to be recompiled?",
            "See the installation instructions;",
            "type: ?Installing the Example package" ] );
      return false;
    fi;
    return true;
  end,
...

76.11-1 IsKernelExtensionAvailable
‣ IsKernelExtensionAvailable( pkgname[, modname] )( function )

For use by packages: Search for a loadable kernel module inside package pkgname with name modname and return true if found, otherwise false. If modname is omitted, then pkgname is used instead. Note that package names are case insensitive, but modname is not.

This function first appeared in GAP 4.12. It is typically called in the AvailabilityTest function of a package (see 76.11).

gap> IsKernelExtensionAvailable("myPackageWithKernelExtension");
true

76.11-2 LoadKernelExtension
‣ LoadKernelExtension( pkgname[, modname] )( function )

For use by packages: Search for a loadable kernel module inside package pkgname with name modname, and load it if found. If modname is omitted, then pkgname is used instead. Note that package names are case insensitive, but modname is not.

This function first appeared in GAP 4.12. It is typically called in the init.g file of a package.

Previously, packages with a kernel module typically used code like this:

path := Filename(DirectoriesPackagePrograms("SomePackage"), "SomePackage.so");
if path <> fail then
  LoadDynamicModule(path);
fi;

That can now be replaced by the following, which also produces more helpful error messages for the user:

LoadKernelExtension("SomePackage");

For packages where the name of the kernel extension is not identical to that of the package, you can either rename the kernel extension to have a matching name (recommended if you only have a single kernel extension in your package, which is how we recommend to set up things anyway), or else use the two argument version:

LoadKernelExtension("SomePackage", "kext"); # this will look for kext.so

76.11-3 LoadDynamicModule
‣ LoadDynamicModule( filename )( function )

To load a compiled file, the command LoadDynamicModule is used. This command loads filename as module.

gap> LoadDynamicModule("./test.so");

On some operating systems, once you have loaded a dynamic module with a certain filename, loading another with the same filename will have no effect, even if the file on disk has changed.

76.12 Testing a GAP package

The tests of a package are named by the TestFile component of its PackageInfo.g file and run by TestPackage (76.12-2). What such a test file must look like is described in 76.12-1. For advice on how to test a package, see https://www.gap-system.org/packages/create/.

ShowPackageVariables (76.12-4) lists the global variables a package introduces, which helps to find undocumented ones and name clashes.

76.12-1 Test files for a GAP package

The (optional) tst directory of your package may contain as many tests of the package functionality as appears appropriate. These tests should be organised into test files similarly to those in the tst directory of the GAP distribution as documented in 7.10.

The component TestFile in PackageInfo.g names a file with a basic test of the package, for example checking that it works as expected and that the manual examples are correct. TestPackage (76.12-2) runs this test, and the GAP developers run it regularly for all packages distributed with GAP. How the file is run depends on its name:

The test should not display any output (e.g. no test progress indicators) except reporting discrepancies and the completion report. Tests which produce extended output and/or require substantial runtime should not be part of this test, but may be placed in the tst directory of the package with instructions on how to run them.

To run a collection of test files and then exit GAP with a suitable status, call TestDirectory (7.10-3) with the exitGAP option set to true:

TestDirectory(DirectoriesPackageLibrary("packagename", "tst"), rec(exitGAP := true));

Test files that need additional packages can be listed in the testfiles component of a package extension, see Section 76.8; TestDirectory (7.10-3) then skips them unless these packages are loaded. If one needs a more sophisticated test file, then it should end with an invocation of ForceQuitGap (6.7-4) with an argument that indicates whether the tests overall passed (true) or failed (false or fail). For example, if the test result is stored in a variable testresult then you can do this:

ForceQuitGap(testresult);

The tests should pass both when all other packages are loaded and when only the needed packages are. With your package in a directory DIR, the following commands check this; each exits with status 0 if the tests passed.

gap -q --packagedirs DIR -c 'LoadAllPackages();
  if TestPackage("packagename") = true then QuitGap(0); fi; QuitGap(1);'
gap -q -A --packagedirs DIR -c 'LoadPackage("packagename" : OnlyNeeded);
  if TestPackage("packagename") = true then QuitGap(0); fi; QuitGap(1);'

76.12-2 TestPackage
‣ TestPackage( pkgname )( function )

It is recommended that a GAP package specifies a standard test in its PackageInfo.g file. If pkgname is a string with the name of a GAP package, then TestPackage(pkgname) will check if this package is loadable and has the standard test, and will run this test in the current GAP session.

The output of the test depends on the particular package, and it also may depend on the current GAP session (loaded packages, state of the random sources, defined global variables etc.).

76.12-3 LoadAllPackages
‣ LoadAllPackages( : reversed )( function )

loads all GAP packages from their list sorted in alphabetical order (needed and suggested packages will be loaded when required). This is a technical function to check packages compatibility, so it should NOT be used to run anything except tests; it is known that GAP performance is slower if all packages are loaded. To introduce some variations of the order in which packages will be loaded for testing purposes, LoadAllPackages accepts option reversed to load packages from their list sorted in the reverse alphabetical order.

76.12-4 ShowPackageVariables
‣ ShowPackageVariables( pkgname[, version][, arec] )( function )
‣ PackageVariablesInfo( pkgname, version )( function )

Let pkgname be the name of a GAP package. If the package pkgname is available but not yet loaded then ShowPackageVariables prints a list of global variables that become bound and of methods that become installed when the package is loaded. (For that, GAP actually loads the package.)

If a version number version is given (see Section Reference: Version Numbers) then this version of the package is considered.

An error message is printed if (the given version of) the package is not available or already loaded.

Information is printed about new and redeclared global variables, and about names of global variables introduced in the package that differ from existing globals only by case; note that the GAP help system is case insensitive, so it is difficult to document identifiers that differ only by case.

Info lines for undocumented variables are marked with an asterisk *.

The following entries are omitted from the list: default setter methods for attributes and properties that are declared in the package, and Setattr and Hasattr type variables where attr is an attribute or property.

The output can be customized using the optional record arec, the following components of this record are supported.

show

a list of strings describing those kinds of variables which shall be shown, such as "new global functions"; the default are all kinds that appear in the package,

showDocumented

true (the default) if documented variables shall be shown, and false otherwise,

showUndocumented

true (the default) if undocumented variables shall be shown, and false otherwise,

showPrivate

true (the default) if variables from the package's name space (see Section 4.10) shall be shown, and false otherwise,

Display

a function that takes a string and shows it on the screen; the default is Print (6.3-4), another useful value is Pager (2.4-1).

An interactive variant of ShowPackageVariables is the function BrowsePackageVariables (Browse: BrowsePackageVariables) that is provided by the GAP package Browse. For this function, it is not sensible to assume that the package pkgname is not yet loaded before the function call, because one might be interested in packages that must be loaded before Browse itself can be loaded. The solution is that BrowsePackageVariables (Browse: BrowsePackageVariables) takes the output of PackageVariablesInfo as its second argument. The function PackageVariablesInfo is used by both ShowPackageVariables and BrowsePackageVariables (Browse: BrowsePackageVariables) for collecting the information about the package in question, and can be called before the package Browse is loaded.

 [Top of Book]  [Contents]   [Previous Chapter]   [Next Chapter] 
Goto Chapter: Top 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 Bib Ind

generated by GAPDoc2HTML