[ext] 4 Writing a GAP Package

[Top] [Up] [Previous] [Next] [Index]

# 4 Writing a GAP Package

### Sections

1. The Structure of a GAP Package
2. The Init File of a GAP Package
3. An Example of a GAP Package
4. Standalone Programs in a GAP Package
5. Installation of GAP Package Binaries
6. Test for the Existence of GAP Package Binaries
7. Calling of and Communication with External Binaries
8. Requesting one GAP Package from within Another
9. Declaration and Implementation Part
10. Package Completion
11. Version Numbers
12. Testing for a GAP Package
13. Writing Documentation
14. Wrapping Up a GAP Package

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 Reference Manual.

## 4.1 The Structure of a GAP Package

Every GAP package is in a subdirectory of the directory `pkg` which itself is a subdirectory of a GAP root directory (see Section GAP Root Directory in the Reference Manual). This directory is called the home directory of the GAP package. The name of this directory (modulo case) is the name of the package. (The package name may have mixed case; but the directory must be all lower case.)

The home directory of the GAP package must contain a file `init.g`. This file contains the necessary GAP code to set up all that the package needs. Section The Init File of a GAP Package explains how such an `init` file must look. If declaration and implementation are separated (see Declaration and Implementation Part) or if completion (see Package Completion) is used there also will be a second file `read.g` which reads in further files.)

In addition, the home directory can contain other files and subdirectories. It is usually a good idea to follow the general example of the subdirectory structure of the GAP root directory. Although this is not strictly necessary, it makes it easier to look through a GAP package and some administrative functions for GAP packages rely on it.

Typically there are at least two subdirectories called `lib` and `doc`. The first contains files with GAP code and corresponds to the GAP library directory. The second contains the manual and documentation of the package.

If the GAP package has stand-alone programs, it should have a subdirectory called `bin` that contains the executables. This directory is subdivided into several others. Refer to section Standalone Programs in a GAP Package on stand-alone programs for a more detailed explanation. In this case there might also be a directory called `src` containing the source code of the stand-alone programs and instructions for their compilation.

## 4.2 The Init File of a GAP Package

The initialization file of a GAP package is a file `init.g` in the home directory of the package. This file tells GAP about the version number of the GAP package, about the documentation and about the actual code. It is a proper GAP file (so syntax and comments are standard), but only the following GAP functions may be called (if further functions are called the loading process, which proceeds in two stages, may not work):

The first command in the file will be a call to `DeclarePackage` (see DeclarePackage in the Reference Manual). This indicates the package's version number and permits to test whether necessary external binaries or other GAP packages are installed. If instead `DeclareAutoPackage` (see DeclareAutoPackage in the Reference Manual) is called, the package will be loaded automatically when GAP has started. (To be exact: GAP loads the library, then reads the users `.gaprc` or `gap.rc` file and then loads the GAP packages which are intended for automatic loading and which are not yet loaded via the users `.gaprc` or `gap.rc` file.)

At the moment automatic loading is only available for the packages listed in the file `pkg/ALLPKG`. This is due to the fact that there is no standard C-Function that will list the contents of a subdirectory.

The next command will be a call to `DeclarePackageDocumentation` (see DeclarePackageDocumentation in the Reference Manual) to indicate the location of the packages documentation to GAP. If instead `DeclarePackageAutoDocumentation` (see DeclarePackageAutoDocumentation in the Reference Manual) is called, the documentation will be read automatically when GAP starts (however `RequirePackage` still has to be used to load the actual package in case the first command in the file is `DeclarePackage`).

If the package requires other GAP packages (see Requesting one GAP Package from within Another) this is followed by calls to `RequirePackage`.

Finally, calls to `ReadPkg` (see ReadPkg in the Reference Manual) will read files which contain the actual GAP functions the package provides.

To separate declaration and implementation parts, for larger packages it is recommended to only load the declaration part from within `init.g` and to use the separate `read.g` file to load the implementation part. See sections Declaration and Implementation Part and Package Completion for details.

For example this is the `init.g` file of a simple ``test'' GAP package:

    # announce the package version
    DeclarePackage("test","1.0",ReturnTrue);

    # install the documentation
    DeclarePackageDocumentation( "test", "doc" );

    # read the actual code.
    ReadPkg( "test", "gap/code.g");

This file installs the GAP package ``test'' in version 1.0. There are no requirements that have to be tested, so `ReturnTrue` (see ReturnTrue in the Reference Manual) is used as test function. It then installs documentation for the package, which can be found in the `doc` subdirectory. Finally, it reads the file `code.g` in the `gap` subdirectory, which contains the actual code.

If the package author wants the GAP package to display a separate banner the printing of the banner must be issued from within a file which will be read via `ReadPkg` from the `init.g` file. If the package is intended for automatic loading we however recommend not to print any separate banner as the user will get a list of the loaded GAP packages at the end of the initialization process.

A GAP package which has been loaded once cannot be unloaded and also cannot be loaded again. However, as with any file individual files may be ``reread''.

- `RereadPkg(` pkg`,` file`) F`
- `RereadPkg(` pkg-file`) F`

  In the first form, `RereadPkg` rereads the file file of the GAP package pkg, where file is given as a relative path to the directory of pkg. In the second form where only one argument pkg-file is given, pkg-file should be the complete path of a file relative to a `pkg` subdirectory of a GAP root path (see GAP root directory in the Reference Manual). Each of pkg, file and pkg-file should be a string.

  When writing (and debugging) a GAP package it may be therefore helpful to use `RereadPkg` on single files or to use a separate ``reader'' file which does not use the `RequirePackage` mechanism.

  ## 4.3 An Example of a GAP Package

  We illustrate the creation of a GAP package by an example of a basic package. (The following assumes that you are using GAP under UNIX.)

  Create the following directories in your home area: `pkg` and `pkg/test`. Inside the directory `test` create the file `init.g` with the lines

      DeclarePackage("test","1.0",ReturnTrue);
      ReadPkg( "test", "banner.g");

  and create a file `banner.g` containing the single line

      Print( "#I reading the GAP package ``test''\n" );

  The next bit is a bit tricky because you have to find out what the root directories of GAP on your system are. This is done by starting GAP and looking at the variable `GAP_ROOT_PATHS`. This a list of directories which GAP searches upon startup for things like the GAP library.

      gap> GAP_ROOT_PATHS;
      [ "/gap/4.0/" ]

  Now start GAP with the command

      gap -l "./;/gap/4.0/"

  The string between the pair of double quotes gives the components of `GAP_ROOT_PATHS` separated by semicolons. We have added at the beginning the string `./` denoting the current directory. This adds the current directory to the list of GAP root directories. Now you can load your GAP package `test`:

      gap> RequirePackage("test");
      #I reading the GAP package ``test''
      true

  This GAP package is too simple to be useful, but we have succeeded in loading it via `RequirePackage()`.

  ## 4.4 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 packages 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.

  ## 4.5 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`.

  The standard GAP distribution contains a GAP package ``example'' whose installation script shows an example way of how to do this.

  ## 4.6 Test for the Existence of GAP Package Binaries

  If an external binary is essential for the workings of a GAP package, the test function called from `DeclarePackage` 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 Reference Manual) for checking for the actual binaries in the path given by `DirectoriesPackagePrograms` (see DirectoriesPackagePrograms in the Reference Manual) for the respective package. For example the ``example'' GAP package uses the following commands to test whether the binary `hello` has been compiled, it issues a warning if not and will only load if it is indeed available.

      DeclarePackage("example","1.0",
        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");
          fi;
          return file<>fail;
        end);

  You might also have to cope with the situation that external binaries will only run under UNIX (and not, say on a Macintosh). See section Testing for the System Architecture in the reference manual for information on how to test for the architecture.

  ## 4.7 Calling of and Communication with External Binaries

  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 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 Reference Manual). The support for this is in its infancy.

  ## 4.8 Requesting one GAP Package from within Another

  It is possible for one GAP package to require another. In principle this can be simply done by calling `RequirePackage` from the `init` file of the one package. However, the other package might be essential and we might want to fail loading if the other package is not available. To achieve this, the tester function in `DeclarePackage` can call `TestPackageAvailability` (see TestPackageAvailability) and check, whether it returns `fail` or not.

  (If the other GAP package is not compulsory, it is also possible to leave out the `TestPackageAvailability` command and only to call `RequirePackage` later in the `init.g` file. In this case the other GAP package will be loaded if available but if this is not the case this will not inhibit loading of the first package. See Testing for a GAP Package.)

  Even if we tested the availability of another package this way, we still need to eventually load the other package using `RequirePackage`. This might initially look like having to do the same work twice. The reason of this split between testing availability and package loading is to ensure the possibility of two GAP packages compulsory requesting each other.

  For example, the `init.g` file of a GAP package ``test'', which requires the GAP package ``example'' in at least version 2.3 would look like:

      DeclarePackage("test","1.0",
        function()
        local a;
          a:=TestPackageAvailability("example","2.3");
          if a=fail then
            Info(InfoWarning,1,"required GAP package ``example'' not available");
          fi;
          return a<>fail;
        end
        );

      DeclarePackageDocumentation( "test", "doc" );

      RequirePackage("example","2.3");

      ReadPkg( "test", "gap/read.g");

  If the GAP package ``example'' was not available (or only available in an older version), the installation of the package ``test'' would fail, no code would be read in and no documentation would be installed.

  ## 4.9 Declaration and Implementation Part

  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 library, it is resolved there by separating declarations (which define categories, operations etc.) and implementations (which install methods) in different files. An implementation file may use declarations 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 same place as `init.g`, this `read.g` file is read only after all the `init.g` files of all (implicitly) required GAP packages are read. Therefore it is possible to separate declaration and implementation for a GAP package in the same way as done for the library by creating such a file `read.g`, and restricting the `ReadPkg` statements in `init.g` to only load those files of the package which provide declaration 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 (in `init.g`) and the implementation part (in `read.g`) of a package.

  ## 4.10 Package Completion

  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 library, the problem there is solved using completion files (see completion files in the reference manual).

  Completion files make it possible to read only short function headers initially which are completed to full functions only if 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 `ReadPkg`) 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. Then use the following command.

- `CreateCompletionFilesPkg(`pkgname`)`

  This will create a new file `read.co` in the GAP package's home directory pkgname (so you must have write permissions there). When reading the GAP package this file is used in place of `read.g` if it exists 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 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 Reference Manual describes the options `-D` and `-N`.)

  ## 4.11 Version Numbers

  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 `VERSION` contains the version number of the version of GAP and also can be checked against (using `CompareVersionNumbers`, see CompareVersionNumbers in the Reference Manual).

  ## 4.12 Testing for a GAP Package

  There are two ways in which one might want to test for a GAP package.

  The first is whether a package is available for loading or has been loaded. This can be done via the function `TestPackageAvailability` (see TestPackageAvailability in the Reference Manual).

  In order to test whether a GAP package name has been loaded already, you can check the global variable `LOADED_PACKAGES` for a record component `LOADED_PACKAGES.(`name`)`.

  ## 4.13 Writing Documentation

  There are now 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 now also an XML-based documentation method that uses the GAPDoc package (see GAPDoc:Introduction and Example). Documentation written according to the format described in chapter The gapmacro.tex Manual format must include a `manual.tex` file to actually run TeX over the manual, the separate TeX files for the manual chapters and the file `manual.six`, produced by running TeX. Whatever system that is used to produce the documentation, there must be a `manual.six` file, since this file is used by the online help to get the index information. If you choose to use a method other than the two methods described above then you need to read chapter Interface to the GAP Help System to find out how to provide the necessary help handler functions and `manual.six` file. By the way, this does not mean that it is in anyway acceptable to provide documentation in just MSWord `.doc` format. Whatever, system you use to produce documentation, the output format must be one or more of plain text, TeX `dvi`, PostScript, PDF, or HTML, and preferably at least both plain text and TeX `dvi`. It may also be helpful to add a preTeXed `dvi` or `ps` file of the package's manual.

  The main directory of the GAP package should also contain a file `INSTALL` or `README` that briefly tells the user what the package does and (if applicable) how to compile and install binaries.

  ## 4.14 Wrapping Up a GAP Package

  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. This includes a `manual.six` file in the documentation subdirectory which is created by TeXing the documentation. 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.

  We use the `zoo` archiver to provide GAP archives and we ask you to use this format for your GAP packages. If `zoo` is not installed on your system, you can get source code for example at ftp://ftp.tex.ac.uk/pub/archive/tools/zoo

  or try a search engine like Google http://www.google.com

  with the keywords: `zoo archiving software`.

  `zoo` by itself archives all files as binary files. This may lead to minor problems when viewing files on DOS or Macintosh systems, which use a different textfile format (CRLF versus LF). To overcome such problems we use the following mechanism for the GAP archive files, which is supported by the `unzoo` we are providing (but is unfortunately not part of the standard `zoo` format):

  All files are archived as Unix versions. (If you are using a Windows PC or a Macintosh the easiest way to achieve this is to ftp the files in text mode to a Unix machine and do the archiving there.)

  When using `zoo` one can add comments to files. Whenever a file gets a comment `!TEXT!` this file will be considered as text (respectively one can enforce binary mode by a `!BINARY` comment) and `unzoo` will extract it accordingly, adding a CR on non-Unix systems.

  If you are using the standard UNIX version of `zoo` the command

      zoo c archive.zoo filename

  will prompt you for a comment to add to the file filename in the archive `archive.zoo`. You will enter for example

      !TEXT!
      /END

  Normally, all files of a GAP package except `dvi` files or special binary files are text files.

  If you are unsure about the format, you can use the ``list'' feature of `zoo` on an existing package (for example `XGAP`) to see all files with comments:

      zoo lc xgap.zoo

  [Top] [Up] [Previous] [Next] [Index]

  GAP 4 manual
  May 2002