How to create contrib packages

Advanced content: for extension authors only

Every so often we find that there are code or data that are shipped with several plugins, addons or skins. This creates several problems:
  1. conflicts can arise between plugins that try to install the same data or code,
  2. multiple versions of the same code can be installed simultaneously in the Foswiki installation,
  3. versions can get out of step
This topic describes how to package libraries of common code and data so that they can be shared between different Foswiki plugins, addons and skins. It also describes how plugins authors can use the facilities of the Foswiki::Func module to check dependencies in their plugins.

Modules packaged in this way are called Contrib modules. While the mechanism described here is mainly intended for packaging code, it could also be used for shipping other contributed modules that have dependencies, such as content or templates.

Note: it would have been nice to re-use one of the standard packaging and distribution mechanisms, such as RPM or CPAN. Unfortunately these make assumptions about the target installation environment that would be incompatible with the way that Foswiki is distributed.

Packaging shared code

Shared code modules are shipped in the same way as plugins and addons i.e. in a zip attached to a documentation topic on foswiki.org. The zip is constructed so that a simple "unzip" command at the root of the Foswiki installation will install the contributed module.

All shared code modules must have a Perl Module (stub), whether they are Perl code or not, and a documentation topic. The Perl module is requires so we can define the dependencies for shared code in a consistent and portable manner.

Step 1: Perl Module (Stub)

This module is required for dependency checking. It will always exist in the Foswiki::Contrib namespace. The simplest possible module contains just a version number. For example, for the JSCalendarContrib module (which is principally Javascript) has the following stub:
package Foswiki::Contrib::JSCalendar;

use vars qw( $VERSION );

$VERSION = 0.96;

1;
  • Note that multiple versions of the same code in a single Foswiki installation is not supported.
  • Note that in order to support dependency checking, $VERSION should always be a valid number.

Step 2: Documentation Topic

There is always a documentation topic that is installed in the System web alongside plugins and addons, as well as in the Plugins web on foswiki.org. This topic is named xxxxContrib (where xxxx is the name of the contribution), and contains the documentation for the module.

Documentation for Perl modules can be hand-authored, or automatically extracted from the code via POD by the BuildContrib module. For other languages it may be hand-authored, or consist of links to documentation topics shipped in the pub directory (e.g. javadoc). In all cases it should follow the template shown in ExtensionTemplate.

Create a new contrib topic:

Note that the standard search summary is used as the code library equivalent of "SHORTDESCRIPTION".

When uploading the documentation topic to foswiki.org, please ensure that the form type is PackageForm and the TopicClassification in the form is set to 'ContribPackage'.

Step 3: Everything else

With the topic and stub module in place, we still have to establish where to put the actual code. The rules are very simple:
  1. Installation should follow the "unzip at the top level" methodology, same as plugins
  2. Shared perl code should be in the Foswiki::Contrib namespace, so should ship in $foswikiroot/lib/Foswiki/Contrib....
  3. Web components that need to be publically accessible via a URL (for example, Javascript) should ship in the pub directory for the contrib topic e.g. %TWIKIWEB/JSCalendarContrib/JScalendar...
  4. bin scripts should ship in bin
  5. Other components that must not be accessible through the web, such as:
    1. code only run once on installation,
    2. sources for statically-built modules e.g. Java,
    3. other code that must be shipped (e.g. to fulfil license conditions), should ship in a top level directory named contrib e.g.
$foswikiroot/contrib/GNURegexp/src/gnu/regexp/RE.java --- used by several plugins
$foswikiroot/contrib/foswiki_dav/dav_foswiki.c --- WebDAVPlugin apache module source
Note: CPAN modules should not be repackaged as Contrib modules. It is much better to document the requirement to install them for the end-user to resolve in a way that is appropriate for their installation.

Checking dependencies

Because all contributed modules have an associated Perl module that includes the version number, the Foswiki::Func function checkDependencies can be used to check dependencies at run-time. For example, your initPlugin might contain:
    if( $Foswiki::Plugins::VERSION >= 1.025 ) {
        my @deps = (
            { package => 'Foswiki::Contrib::JSCalendar', constraint => '>= 0.96' },
            { package => 'Foswiki::Contrib::Attrs', constraint => '>= 1.00' },
        );
        my $err = Foswiki::Func::checkDependencies( $pluginName, \@deps );
        if( $err ) {
            Foswiki::Func::writeWarning( $err );
            print STDERR $err; # print to webserver log file
            return 0; # plugin initialisation failed
        }
    }
Note the use of a print to STDERR to output error messages. This will print a message to the Apache log file if the installation is using Apache. This is in addition to writing to warning.txt.

Tip: If you use lazy loading to delay loading modules until they are actually required, you may also have to make the checkDependencies call lazy, as it internally calls use on each dependency.

Putting it all together

What goes in a module?

One key decision you will have to make is what to bundle together, and what to ship as separate modules. To make this decision, consider the following factors:
  1. The pieces of a module should fit together logically, and fit with the module name. Don't package a search routine in a module called "SortRoutines".
  2. If the different submodules of the common code have tight interdependencies, then ship them together.
  3. Each common code module has to be separately installed by the end user; more modules means more work for them.
  4. Don't be afraid to package very small pieces of reusable code separately if they don't fit perfectly within a bigger module, but are very reusable.

To SVN or not to SVN

You are strongly recommended to use the Foswiki SVN repository (trunk) for managing your code. If you can't get to this repository, then you are strongly recommended to use a local repoaitory.

Build support

A BuildContrib module has been developed to help you ship shared code, plugins, skins and add-ons. This module is described in the BuildContrib topic. The Build module supports testing using Test::Unit, automatic extraction of documentation using POD, manifest based packaging, and automatic upload mechanisms,
Topic revision: r2 - 13 Mar 2010, ArthurClemens
The copyright of the content on this website is held by the contributing authors, except where stated elsewhere. See Copyright Statement. Creative Commons License    Legal Imprint    Privacy Policy