Blog of Raivo Laanemets

Stories about web development, consulting and personal computers.

Prolog pack development experience

On 2015-02-17
By Raivo Laanemets

There has been some recent discussion about the (best) practices for developing and publishing packs. For informative purposes I have decided to document my own practices and workflow here.

So far I have published 7 packs:

All of them share the similar project structure and development workflow.

Project structure

The minimal pack contents is specified in here. I also add:

The tests directory contains the test code but the main reason for inclusion is that it provides additional example code. I'm not entirely sure if that has actually been a good idea as there have been ideas of running tests automatically after installation. That might not work for all of my packs.

The README file contains the project documentation, smaller examples with description and various other information. This is the most important piece of documentation the pack user will read (besides the code). This is also what the pack user first sees when browsing the pack on the SWI homepage or in a GitHub/Bitbucket-styled project/repo hosting site.

The LICENSE file is required as the pack meta-data does not have any field for the license information.

Module name prefixing

In order to avoid module name clashes, name prefixes have to be used. For example, for the simple_template pack I chose the st_ prefix and put the module files under the prolog/st directory. Subdirectory is not strictly required with packs but it made uninstall easier and avoided file name conflicts before packs were added to SWI and 3rd party libs were installed directly to the SWI libraries location.

For single-module packages I have not used a prefix as I have named the module the same as the package. It would be rather counter-intuitive and confusing to not do that. This should avoid unexpected name clashes.

README file structure

The file contains:

Build system

I expect the pack archive file to be buildable from the project repository. I generally do not expect it to be buildable from the files included in the pack archive file itself as the build process might require a specific environment or a toolset. For build/publish automation I use make and I have a fairly simple set of targets:

The example file can be found from here. Care must be taken to not include the Makefile in the pack archive file. If you include the project Makefile in the pack, then the pack is treated as a foreign even when it contains no native code. I learned it by the hard way by having a Makefile included and seeing all the weird results when make all was run in a location where it was not expected.


Pack versioning is quite arbitrary at the moment. I prefer semantic versioning. Michael Hendricks prefers it too and actually points that out in his projects' README files. However, version numbers are only used during publishing and you cannot easily install a specific version or set dependencies by version numbers/ranges.

Publishing workflow

  1. Write code and tests.
  2. Make sure tests run from the project root.
  3. Write containing the information above.
  4. Generate API docs, upload to somewhere (unnecessary?).
  5. Include API docs link in the README (unnecessary?)
  6. Build pack archive file.
  7. Use pack_install with the file.
  8. Verify installed files.
  9. If the last step fails, do changes and repeat from (6) until things work.
  10. Tag version 0.0.1 in the project repo.
  11. Upload the pack file somewhere.
  12. Publish the pack by using the remote URL during install.

Republishing/update workflow

  1. Update code and tests.
  2. Regenerate API docs and reupload (unnecessary?)
  3. Update API and Changelog sections.
  4. Pump the version number according to the changes and tag commits in the repo.
  5. Rebuild the pack archive file.
  6. Upload the pack file somewhere.
  7. Publish the pack by using the remote URL during install.


Install by linking

This allows to install a pack from a local location, without copying files but by symlinking to the pack project root. This enables make/0 based refresh of the pack code. This is incredibly useful when developing a pack and checking that dependant packs and projects are still working with the changes made.

Currently it possible to link manually by symlinking:

~/lib/swipl/pack/pack_name -> ~/pack_project_root

However, there is a gotcha. If you happen to run pack_install(pack_name), it wipes your project files!

Project-local packs

This approach allows to load pack code from an arbitrary location in the same way as the pack was installed by normal means. When the pack is installed into the packs/pack_name directory (relative to the current working directory) then it can be made available in the project by adding a clause to the user:file_search_path predicate (into the main file, before any use_module call that uses the pack):

:- asserta(user:file_search_path(library, 'packs/pack_name/prolog')).

And then loading a module from the pack works the same as the pack was installed into a standard location using the pack_install predicate.


On 2015-02-18 by RLa:
Thanks to Anne for remaining me to include user:file_search_path hack.
On 2015-02-18 by Michael Hendricks:
This was helpful. There's a lot of similarity between our workflows. I'll probably adopt your symlinking technique for development. Thanks.
On 2015-04-27 by Anne Ogborn:
If you are hosting on github, note that github will make the archives for you. I somehow didn't understand this. and here's the archive format Note that GET /repos/:owner/:repo/:archive_format/:ref 302's to something like
On 2017-10-28 by Chris Mungall:
Thanks for putting this up! I too use the symlink method, but it's a bit clunky/dangerous for the reasons you mentioned. Are you aware of any alternatives for working in development mode? I don't want to do a pack_install every time I make local/experimental changes to a pack that I'm using in another program.
On 2017-11-06 by RLa:
@Chris I think that the new attach_packs predicate should make it easier to use a pack from the given directory. I have not used it myself tho.