Chapter 2. Project Reference

This section describes a quokka project definition in detail.

A project is defined by one or more files, but is usually:

<project>-quokka.xml

Mandatory. This is the project file and is the key quokka file. It defines the artifacts, dependencies and plugins that make up the project.

<project>-quokka.properties

Optional. This is the project level properties file and is used to configure the project and plugins, for example setting the java source level that the project is using. Note that properties can be specified from other locations (see Properties below). Note: In general it is preferable to omit this file and define properties within <project>-quokka.xml.

<project>.xml

Optional. This is a standard Ant file that is imported automatically into your project, providing a mechanism to add your own bespoke targets to the project without having to write your own plugin.

The project files are linked by naming convention, so the value of <project> must match for them to be recognised as a single project. By default, <project> is build and quokka looks for it in the current directory. This can be overridden by the -f command line option. See Command line options for more information.

2.1. The Project File

As outlined above, the project file is the key quokka file. It is an XML document with a DTD available for validation purposes. The sub-sections below outline the elements in detail. Below is a typical example:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE project PUBLIC "quokka.ws/dtd/project-0.2" 
                  "http://quokka.ws/dtd/project-0.2.dtd">

<project default-target="install">
    <artifacts group="mycompany.myproject" version="0.1-ss">
        <artifact paths="runtime"/>
    </artifacts>

    <bootstrap core="[0.3,0.4)"/>

    <dependency-set>
        <profile id="skiptest" description="Skips tests"/>

        <!-- ================================================================== -->
        <!-- The entries below add dependencies to project paths                -->
        <!-- ================================================================== -->
        <dependency group="junit" version="3.8.2" paths="test-compile"/>
        <dependency group="apache.log4j" version="1.2.15" paths="compile, runtime"/>

        <!-- ================================================================== -->
        <!-- The entries below add a number of plugins that introduce targets   -->
        <!-- to the project                                                     -->
        <!-- ================================================================== -->

        <!-- Includes plugins for resources, compilation & packaging of a .jar file -->
        <dependency-set group="quokka.depset.jar" version="0.3"/>

        <!-- Runs all developer reports and produces an HTML summary -->
        <plugin group="quokka.plugin.devreport" version="0.3"/>

        <!-- Adds jUnit support for running unit and integration tests -->
        <plugin group="quokka.plugin.junit" version="0.3" profiles="!skiptest"/>
        <plugin group="quokka.plugin.junitreport" version="0.3" profiles="!skiptest"/>

        <!-- Adds Cobertura support for producing code coverage reports -->
        <plugin group="quokka.plugin.cobertura" version="0.3" profiles="!skiptest"/>

        <!-- Adds the help plugin -->
        <plugin group="quokka.plugin.help" version="0.3"/>
        <plugin group="quokka.plugin.repository" version="0.1"/>
        <plugin group="quokka.plugin.jalopy" version="0.3"/>

        <property name="q.project.java.source" value="1.4"/>
        <property name="q.project.java.target" value="1.4"/>
    </dependency-set>
</project>

The first thing to note about a project file is the DOCTYPE declaration. It is mandatory for quokka projects and is used both for validation and to ensure the DTD version is supported by the current installation.

The project element is the root of document.

Attributes are:

name

The name of the project. Optional if the project has artifacts and then the group of the artifacts element is taken as the default name.

default-target

The default target if none is specified on the command line

A project may contain 0 or 1 of artifacts anddependency-set elements and 0 or more bootstrap elements.

2.1.1. Artifacts

The artifacts element defines the artifacts generated by the project, specifically those that can be installed in the repository for use by other projects.

Attributes are:

group

Mandatory. The group to which the all artifacts for this project belong. It is similar to a Java package name with levels separated by '.' characters. There are naming standards for groups, most notably that all artifacts in a group must be released together with the same version number.

version

Mandatory. The version of the artifacts produced in the project. There is a standard format for versions (see Versioning) that should be used where possible to permit comparisons and ranges of versions. Briefly, the format is major.minor.micro.update-qualifier~repositoryVersion. e.g. 1.1-m01 is a valid version.

type

Optional. Defaults to jar. Note: additional types may be supported via plugins that register the types with the repository.

Each artifacts element may have 1 or more artifacts defined via nested artifact elements. Attributes are:

name

Optional. The name of the artifact. Defaults to the last segment of the group if not specified.

description

Optional. A description of the artifact.

paths

Optional. Specifies the project paths to export to the repository when the artifact is installed (it only exports the path definition, not the artifacts themselves). Commonly, the runtime path is exported and contains any dependencies the artifact requires at runtime. However, multiple paths may also be exported. Furthermore, paths may be renamed on export, using a colon separator. e.g. paths=runtime:jdk14, jdk15 would export the runtime path as jdk14 and the project path jdk15 unchanged to the repository.

2.1.2. Dependency Sets

A dependency set is a reusable set of build configuration, including paths, plugins, properties and dependencies. Dependency sets are often created as separate projects in their own right and then included in other projects as nested sets. See Nested Dependency Sets for more information. However, each project has its own root dependency set, declared by the dependency-set element in the project. It consists of one or more of the nested elements listed below.

2.1.2.1. Paths

A path is graph of artifacts from the repository. Any number of paths can be defined in a project and may be passed to plugins as arguments. Plugins may also define paths, for example the compile, test and runtime paths are defined by the life cycle plugin.

Creating a path is divided in two parts. Firstly, the path must be declared using a path element (or be declared by a plugin that has been included). Secondly, artifacts are added to the path via one or more dependency declarations.

The path element defines a path. Attributes are:

id

Mandatory. The id of the path.

description

Optional. Description of the path usage.

descend-default

Optional. Controls the default handling of transitive dependencies (dependencies of the dependencies added). If true, when a dependency is added to the path, all mandatory dependencies of the dependency will be recursively added. If false, only the dependency itself will be added. Defaults to true. Note: this value can be overridden on a case by case basis when adding the dependency.

mandatory-default

Optional. Controls whether the dependencies added to the path are considered to be mandatory by default. Mandatory dependencies are always included with the parent when transitive dependencies are used. Optional dependencies must be specified explicitly. Defaults to true.

Once the path is defined, artifacts are added to it via dependencies.

2.1.2.2. Dependencies

A dependency refers to an artifact in the repository and is assigned to one or more paths. It is defined by the dependency element. Attributes are:

group

Mandatory. The group of the dependency

name

Optional. The name of the dependency. Defaults to the last segment of the group if not specified.

type

Optional. Defaults to jar.

paths

Optional. Specifies the paths that the dependency should be added to. The format is the shorthand notation for path specification (see below).

2.1.2.3. Path Specifications

Path Specifications allow a graph of dependencies to be added from one path to another. In particular, it allows complete control over what transitive dependencies are included (dependencies of dependencies).

There are 2 forms of path specifications, shorthand and formal. The shorthand notation makes simple things simple, while the formal form can be clearer for complicated use cases and works with profiles.

Lets consider the formal notation first. It is defined by a nested path-spec element. Attributes are:

from

Optional. The id of the path that the dependencies are being copied from. Defaults to runtime.

to

Mandatory. The id of the path that the dependencies are being copied to. Note: Must be omitted when applied to plugin paths.

descend

Optional. If true, mandatory transitive dependencies will be included. If false, just the parent dependency itself will be added. Defaults to the descend-default value of the path referred to by the to attribute.

mandatory

Optional. If true, the path copied will be marked as mandatory on the to path. It will then be included by default when a 3rd party uses the to path. If false, it will be marked as optional on the to path and 3rd parties must specify it as an option to include it when using the to path. Defaults to the mandatory-default value of the path referred to by the to attribute.

options

Optional. A graph of optional dependencies from the from path. It is specified as a string of options, nested by parenthesis. e.g. Consider an example where testng depends on qdox and bsh, and that bsh depends on log4j and that all dependencies are optional. To specify these dependencies, you would use options="testng(qdox, bsh(log4j))". The values used are the name attributes of the dependencies. If the names are not unique within particular from path, you must specify the group as well, separated by a colon. e.g. apache:log4j instead of log4j.

Options can also be used to exclude a transitive dependency. This is useful in siutations when the meta data incorrectly states a depenendency is mandatory, where it is in fact optional. To exclude it, prefix the option with a minus sign. e.g. use options="testng(-qdox)" to exclude the qdox dependency when using testng.

Note: the options will be added regardless of the value of the descend attribute.

Let's consider an example before moving on to the short hand form. The life cycle plugin defines a number of paths including the following (they are included automatically when the life cycle plugin is used):

<path id="compile" descend-default="false" description="Compilation class path"/>
<path id="runtime" descend-default="true"  description="Runtime class path"/>

Now consider we have the following artifacts in the repository, with the dependencies shown by indentation:

hibernate (runtime path):
    dom4j: mandatory, descend
        jaxen: mandatory, descend
        xml-apis: optional, descend
    commons-logging: mandatory, descend
    jgroups: optional, descend
    c3p0: optional, descend

Now in our project we define a dependency upon hibernate:

...
<dependency-set>
    <dependency group="hibernate" version="x">
        <path-spec from="runtime" to="compile"/>
        <path-spec from="runtime" to="runtime"/>
    </dependency>
</dependency-set>
...

The first path-spec says add the dependencies from runtime path of hibernate to our project's compile path. No descend attribute is specified, so it uses the descend-default value of false. Without descending transitive dependencies, only hibernate is added. Result: compile path = hibernate.

In the second path-spec, it says to add the same dependencies to the runtime path. Again, no descend attribute is specified, but this time the descend-default value is true on the runtime path. Therefore, all mandatory transitive dependencies are included as well. Result: runtime path = hibernate, dom4j, jaxen, commons-logging.

This is a very common example. For compiling, you only need the direct dependency you are using, but at runtime you need the transitive dependencies as well.

Now let's add some optional dependencies. At runtime, we've decided we'll use the c3p0 connection pool and as we're targeting an old JVM, we need the xml-apis as well. To do this we modify the second path-spec as follows:

<path-spec from="runtime" to="runtime" options="c3p0, dom4j(xml-apis)"/>

The result: runtime path = hibernate, dom4j, xml-apis, jaxen, c3p0

Path Specifications allow complete control over transitive dependencies to any level. You can even set descend to false on the top level thereby excluding everything by default and then add in the dependencies you want one by one. However, assuming the mandatory dependencies have been defined correctly, you can usually just include the defaults and just add any options.

Now that we understand the formal form of path specifications, let's rework the example using the shorthand notation.

2.1.2.3.1. Shorthand Path Specifications

Shorthand path specifications are orthogonal to formal specifications, but as they name suggests are a shortened form of achieving the same thing.

The shorthand form is specified via the paths attribute on the dependency element. It has the following grammar: to[!|?][+|<][from][(options)], where:

to

Is the to path id. It is mandatory when adding dependencies to a project path, but must be omitted when configuring a plugin path.

?

If present, mandatory is false, otherwise the path default

!

If present, mandatory is true, otherwise the path default

+

If present, descend is false, otherwise the path default

<

If present, descend is true, otherwise the path default

from

If present it is the from value, otherwise runtime is the default

options

Used if present: they are specified in the same format as the formal specification above.

Given the above definition, we can translate the formal specifications. For the first example without options it becomes:

<dependency group="hibernate" version="x" paths="compile, runtime"/>

That's it. As we are using hibernate's runtime path, it is the default and we don't have to specify it (this is the common case as most libraries do not have multiple runtime configurations). Now let's add the options.

<dependency group="hibernate" version="x" paths="compile, runtime(c3p0,dom4j(xml-apis))"/>

As you can see, shorthand notation makes the common cases more succinct and readable.

2.1.2.4. Plugins

Without plugins, the quokka core itself doesn't actually do anything. You have to add plugins to make quokka function, selecting plugins suitable for your task. Common sets of plugins can be bundled up as a dependency set. e.g. the jar dependency set includes the plugins necessary to build and package a .jar file from java sources.

Plugins can contribute both paths and targets to a project. Plugins themselves are essentially a special dependency, so are stored in the repository along with all the other artifacts.

When you include a plugin, it will enable a set of targets by default. You can then add additional targets, or override the defaults completely and pick the targets you want. As each target defines its own dependencies, you only include the plugin dependencies for the targets you enable.

Targets from plugins may be self contained, or depend upon other targets defined within or outside the plugin. Commonly, targets will implement abstract targets defined in other plugins. For example, the javac plugin implements the abstract compile target from the life cycle plugin.

Alternatively, plugins can be templates. Templates need to be instantiated in your project, binding them to a target. For example, the xml plugin has a transform target template. This allows you to instantiate multiple instances, corresponding to multiple xml documents in your project, assigning the transforms to separate targets.

Plugins are defined by the plugin element. Attributes are:

group, name, version

Same as for dependency above

use-defaults

Optional. If true, the default targets specified by the plugin will be enabled automatically. If false, no targets will be enabled automatically - you must specify the targets you want explicitly. Defaults to true.

targets

Optional. A comma separated list of targets to use from the plugin. If use-defaults is true, it will add the targets in addition to the defaults, otherwise it will enable only those listed. Note: this is a shorthand form of the nested target elements described below.

templates

Optional. This is only used in assocation with the script plugin. Any template target that is used in a script must be declared first in this attribute.

The above covers the basic configuration of plugins. However plugin has 2 nested elements, path-spec and target with further options.

The path-spec element allows you to control the plugin dependencies. Each plugin target uses one or more plugin paths (you need to consult the specific plugin documentation to determine the paths used). Each of these paths can then be controlled by a nested path-spec in exactly the same was described above in the Path Specification section. The only difference is that the to id must not be specified for plugins.

One or more target elements may be nested within the plugin element. For a template target, this is the mechanism for instantiating the target. For other targets, it allows additional configuration such as adding dependencies and aliases. Attributes of the target element are:

name

Mandatory. The name of the target as declared in the plugin. In the case of a template, this is the name you want to give the target instance, the template itself should be named with the template attribute.

depends

Optional. A comma separated list of targets that this target depends on. If the targets are not defined in the plugin they must be fully qualified with the plugin name space. e.g. lifecycle:package, not package. If the plugin declared other dependencies, these will be added to them.

dependency-of

Optional. A comma separated list of targets that depend on this target. If the targets are not defined in the plugin they must be fully qualified with the plugin name space. Together with depends, this allows you to insert this target in a precise location in the build sequence.

alias

Optional. An alias for the target name. Plugin targets by default are fully qualified with the plugin name space to prevent name clashes. However, this can get unwieldy at the command line. Setting an alias allows you to define a short meaningful name within the project. Note: plugins can also alias targets and commonly do so for major targets such as clean, package and install.

template

Optional. For templates only. The name of the template target in the plugin

prefix

Optional. For templates, it defines the prefix for any properties the template uses for configuration. Prefixes should be unique (including prefixes used by default targets). For standard targets, it must match the pre-defined prefix and is only useful when defining nested property elements.

Targets may also contain nested property elements. Such properties will automatically be prefixed with the prefix defined in the parent target element.

The following is a typical example of configuring a plugin with a template target. In this case, we are using the JavaCC plugin to generate a parser for a simple grammar:

<plugin group="quokka.plugin.javacc" version="?">
    <target name="myjavacc" template="javacc-main">
        <property name="in" value="src/TestGrammar.jj"/>
        <property name="sourcesRoot" value="${q.project.targetDir}/generated-sources"/>
        <property name="out" value="${myjavacc.sourcesRoot}/test/quokka/profile"/>
    </target>
</plugin>

2.1.2.5. Nested Dependency Sets

Dependency sets may nest other dependency sets. This is the mechanism quokka uses to share build configurations amongst projects. The nested dependency sets can include all the elements defined in this section, including plugins, dependencies and paths. Additionally, they can include a properties file and build resources, so that any targets included by the dependency set can come pre-configured.

This is especially useful in a corporate setting, or anywhere that you have multiple projects. You can define a dependency set that includes your standard build tools pre-configured to corporate standards. e.g. A dependency set including a checkstyle plugin together with your custom checkstyle configuration file as a build resource. As dependency sets may nest other sets, you can make the sets as granular as you like.

When using nested dependency sets in your project, they cannot contain any elements, they must just be a reference to the dependency set in the repository. In the future, this may be relaxed to allow overriding of certain elements with the nested set. Below is an example of a nested dependency set declaration. It's attributes are group, name and version, as per the dependency element above.

...
<dependency-set>
    <dependency-set group="quokka.depset.jar" version="0.1"/>
</dependency-set>
...

2.1.2.6. Profiles

Profiles are a mechanism for providing alternative build configurations for the same project. A common example would be to add a skiptest profile that disables all testing related targets. Another example might be producing differently configured artifacts for development, test and production environments.

Profiles must be declared within a dependency-set with a profile element. Attributes are:

id

Mandatory. The id of the profile

description

Optional. A description of what the profile does.

A sample profile definition:

<profile id="skiptest" description="Skips tests, disabling all test-related targets"/>

Once defined, the profile can be assigned to most project elements by using the profiles attribute. The profiles attribute may contain an expression using profile ids, logical operators and grouping with brackets.

Logical operators are:

!

Logical NOT.

& or +

Logical AND. (The + symbol is supported to prevent the need for XML escaping)

|

Logical OR.

()

Brackets can be for grouping

For example, to exclude the jUnit plugin when the skiptest profile is enabled would be:

<plugin group="quokka.plugin.junit" version="0.1" profiles="!skiptest"/>

Enabling profiles is via the profiles property, commonly set at the command line as:

$ quokka -Dprofiles=skiptest

Multiple profiles can be enabled simultaneously - just supply a comma separated list of profile ids for the profiles property.

Properties inside properties files can also have different values depending on what profiles are enabled. Prefix the profiles inside square brackets in front of the property name. e.g. [skiptest]someMessage=Tests are enabled.

2.1.2.6.1. Automatic Profiles

Quokka automatically defines some profiles and automatically enables them depending on the project environment. You can use any of the automatic profiles to configure your project. The current automatic profiles ares:

source1.x

Where x is 1 to 6. The profile is enabled when the q.project.java.source value matches. i.e. source1.4 is enabled when q.project.java.source=1.4. All other source1.x profiles would be disabled.

target1.x

Same as source1.x above, but depends on the q.project.java.target value

java1.x

Same as source1.x above, but depends on the Ant.java.version value

hasParent

Is enabled in child projects in multi-project builds. It allows the child to enable different targets when called stand-alone versus by a parent project

os*

Enabled depending on the current operating system. Possible values are osWindows, osWin9x, osWinnt, osOs2, osNetware, osDos, osMac, osTandem, osUnix, osOpenvms, osZos and osOs400.

Note that multiple values can be set simultaneously. e.g. OS X will set both osMac and osUnix.

Combining automatic profiles with the logical operators above gives an effective way of customising builds for certain enviroments. For example, the Script plugin uses this to control how Javascript is made available. A profile expression of !java1.6, adds Rhino using Apache BSF. An expression of osMac + java1.6 adds Rhino using JSR223 and the default adds nothing (picking up the bundled Rhino version on other 1.6 platforms).

2.1.2.7. Overrides

It is sometimes desirable to override the version of artifacts, such as those declared as plugin dependencies. In fact, it may be essential if there is a version conflict between your dependencies and plugin dependences (this is quite rare in quokka as each target has an isolated class loader).

However, conflicts may still occur, or you might just want to normalise the version of a particular library on the same version. Quokka provides two mechanisms to do this.

The first is very specific, and allows a specific version on a specific path to be overridden. It is achieved by adding the version to a path specification.

For example, to override the c3p0 version to 1.1, and the xml-apis version to 2.2 for the path spec below:

<path-spec to="runtime" options="c3p0, dom4j(xml-apis)"/>

Becomes:

<path-spec to="runtime" options="c3p0@1.1, dom4j(xml-apis@2.2)"/>

Naturally, care must be taken to only override versions to those that are compatible. Note that transitive dependency versions might also change as a result.

The second mechanism for overriding has a much wider scope and can change a range of versions to another version. It is achieved using a nested the override element within a dependency-set. Attributes are:

group

Mandatory. The artifact group to match.

version

Optional. A version range union to match. See Versioning below for more details on version ranges.

name

Optional. The artifact name to match. If not present, all names match within the group.

type

Optional. The artifact type to match. If not present, all types match.

paths

Optional. The project paths to match. "*" is a wildcard matching all project paths. If not present, the override will not apply to project paths.

plugin-paths

Optional. The plugin paths to match. "*" is a wildcard matching all plugin paths. If not present, the override will not apply to plugin paths. The format is pluginGroup[:pluginName]=path1[:pathn]. Multiple plugins may defined in a comma separated list.

with

Mandatory if with-paths not supplied. The overriding version to set all matches to.

with-paths

Optional. The overriding path specification. The path specification should not include a to id and will match against any dependency with a matching from id. The options and descend and mandatory value will then be overwritten with those supplied.

The following example will set all versions of log4j 1.2.x to 1.2.15 within project paths.

<override group="apache.log4j" paths="*" version="[1.2.0, 1.3.0)" with="1.2.15"/>

Note: versions must be in the standard format to support ranges, otherwise specific versions must be listed.

The following example will force the xml-apis to be automatically added any time dom4j is used in a plugin path.

<override group="dom4j" name="dom4j" plugin-paths="*" with-paths="runtime(xml-apis)"/>

Notes:

  1. If you use an override that applies to a project path, it will be exported automatically to the repository defintion during packaging.

  2. Quokka does not provide automatic conflict resolution. This is to ensure that your project has as few dependencies as possible. Automatic conflict resolution requires all versions of dependencies to be touched before culling the conflicts. On the other hand, specifying overrides allows the culling to happen up front.

2.1.3. Bootstrapping

Quokka allows several versions of itself to be installed simultaneously. Bootstrapping can then be configured so that at startup quokka can automatically select the correct version of itself to use. Bootstrapping is not restricted to the quokka version itself, but also allows a specific JDK to be selected, along with JVM arguments.

Bootstrapping is defined by the bootstrap element, a nested element within the project element. Below is an example that would only build if quokka version 0.1 and a Windows java 1.6 implementation were available:

<bootstrap>
    <cores>
        <core version="[0.3,0.3]"/>
    </cores>
    <jdks>
        <jdk jvm-args="-Xmx512m" spec-version="[1.6, 1.6]">
            <sysproperty name="os.arch" value="Windows" required="true"/>
        </jdk>
    </jdks>
</bootstrap>

Note: the above is a little long-winded as it is designed to work in conjunction with profiles. However, there is a shorthand form that accepts common attributes within the bootstrap element itself. e.g. the following requires quokka 0.1 to 0.2, a heap of 512MB and a Java version from 1.4 to 1.6 to build.

<bootstrap core="[0.1,0.2]" jvm-args="-Xmx512m" spec-version="[1.4,1.6]/>

The bootstrap element supports the following attributes (all are optional):

core

A shorthand way to specify the core version (as a version range union).

file

A file containing the bootstrap configuration. This can be useful if the bootstrapping is complicated, allowing it to be split out and shared across modules.

java-version, jvm-version & spec-version

Matches the JDK system properties java.version, java.vm.version and java.specification.version respectively. The value specified is a version range union. See Versioning below.

java-vendor, java-jvm-vendor

Matches against the JDK system properties java.vendor and java.vm.vendor respectively using a string comparison.

jvmArgs

Checks that the quokka JVM was started with the specified arguments. Note: If additional arguments were supplied it is still considered a match

A bootstrap element can have nested cores and jdks elements. The cores element allows you to specify the quokka version that the project requires. Attributes are:

version

A version range union of quokka versions.

Nested inside the cores element is a core element. It has the same attributes as the cores element, using any cores attributes as defaults. This arrangement allows flexibility when using profiles.

The jdks element lets you specify constraints on the JDK used to build the project. It contains nested jdk elements and like the cores element, any attributes declared at the jdks level will be the defaults for jdk elements within. Attributes are:

jvmArgs

Optional. See above.

java-version, jvm-version & spec-version

Optional. See above.

java-vendor, java-jvm-vendor

Optional. See above.

System properties can also be used as constraints be specifying nested sysproperty elements within the jdk elements. Attributes are:

name

Mandatory. The property name

value

Mandatory. The property value. Matches using string comparison

required

Optional. Specifies that the JDK must have the property for it to be considered a match.

If you use jdk constraints, you have to tell quokka where to find jdks on your system. To do this edit the bootstrap.xml file within the bootstrap directory within the quokka installation directory. Below is an example file:

<bootstrap>
    <jdks>
        <jdk location="C:\Program Files\Java\jdk1.6.0_02\bin\java.exe"/>
        <jdk location="C:\Program Files\Java\jdk1.6.0\bin\java.exe"/>
        <jdk location="C:\Program Files\Java\jrockit-R27.3.1-jdk1.6.0_01\bin\java.exe"/>
    </jdks>
</bootstrap>

Note that the order is important. The first JDK in the list that matches will be used for bootstrapping.

Another use of the bootstrap element is to provide an additional library or libraries to the quokka class path. At present, the only reason you would want to do this is to provide a custom repository implementation. All other dependencies should reside in the repository itself. To add a bootstrap dependency, use the boot-dependency element nested within the bootstrap element. Attributes are:

group, name & version

The standard attributes to identify an artifact.

file & url

Optional, mutually exclusive. The location of the dependency. This is useful if you need to specifiy a boot dependency, but have no control over the installation environment. Note however, that the forking of a new JVM will always occur in this case. If you do not specify a file or url, you must copy the artifact to your quokka installation directory, under the lib directory with a name of group_name_jar_version.jar

Tip

Combining bootstrapping and profiles can be a powerful way of testing your product on multiple JDKs.

Tip

When using bootstrapping it is a good idea to modify the quokka startup script to match the most common bootstrap configuration. This improves performance as quokka will not have to fork a new JVM if the defaults matching the bootstrapping configuration.

2.2. Properties

Properties are used to configure all aspects of quokka including plugins. Properties are loaded from the following sources if they exist (first is highest precedence if there is a conflict):

  • Properties specified on the command line with -D

  • Properties specified in quokka.properties from within the preferences directory

  • Project properties (usually build-quokka.properties in the project base directory, or within build-quokka.xml itself)

  • Properties bundled with any dependency sets included in the project

  • Default properties defined by the plugins

Tip

With so many sources of properties it can become confusing where a particular property originated from. If in doubt, include the help plugin in your project (quokka.plugin.help) and then type quokka help from the command line. This will generate a HTML report, including all properties. If you hover your mouse over a property, a tool tip will appear showing the origin of the property

Properties are pretty much in the standard Java property format, although there are a few extensions.

Firstly, properties can reference other properties using ${property} notation. In the example below, key will resolve to a value of "bar":

foo=bar
key=${foo}

A special comment line can define an alias, allowing you to use the alias followed by a '!' character in place of a prefix. In the example below, repo is an alias for q.repo..

#{repo=q.repo.}
repo!snapshot.rootDir=${user.home}/.quokka/snapshots
repo!snapshot.hierarchical=true

Aliases are primarily used to shorten what can otherwise become long property prefixes.

Note that properties are case sensitive.

Properties can also contain limited expressions. Property expressions are of the form ${@expression}. The following expressions are supported:

literal

Grammar: '<value>'. e.g. below, foo is set to a value of bar. In this case foo=bar would be a better choice, however literals are useful in more complex expressions. Literals can also be concatenated with a '+' operator.

foo=${@'bar'}
ref

Grammar: ref <propertyref-expression>. Turns a literal into a property expression. e.g. below, ref turns the literal 'foo' into a property reference of foo, so key1 resolves to bar. Again, key1=bar would be easier in this particular case.

foo=bar
key1=${@ref 'foo'}
setifdef

Grammar: setifdef <propertyref-expression>. Sets a given property to the value of another property if it is set, otherwise the property is left undefined. e.g. below, key1 will resolve to bar, but key2 will be undefined as if the key2 line never existed.

foo=bar
key1=${@setifdef foo}
key2=${@setifdef foo2}
ifdef

Grammar: ifdef <propertyref-expression> ? <true-expression> : <false-expression>. e.g. the following sets key1=fooset if foo is set, or key1=foounset if foo is unset.

key1=${@ifdef foo ? 'fooset' : 'foounset'}

Expressions can be nested and combined to do more useful things than the example above.

Wildcard properties are also supported to copy all properties with a given prefix to another prefix. e.g. below will result in myTarget.key1=foo and myTarget.key2=bar being created.

defaults.key1=foo
defaults.key2=bar
myTarget.*=defaults

Combined with expressions, wildcards can allow some fairly powerful defaults handling. For example, the xml plugin defines the following properties in the plugin:

q.xml.transform.defaults.transformClassPath=xml-transform
q.xml.transform.defaults.catalogPath=xml-catalog
q.xml.transform.defaults.targetDir${q.project.targetDir}/xml
q.xml.transform.defaults.sourceDir=${q.project.sourceDir}/xml

prefix.defs=${ifdef prefix.defaults ? prefix.defaults : 'q.xml.transform.defaults'}
prefix.*=${prefix.defs}

Above, if prefix.defaults is defined by the user, all properties starting with that prefix are used as defaults, otherwise q.xml.transform.defaults are used. This allows users to define different sets of defaults for different transforms, such as docbook processing versus xhtml processing. Which set of defaults is used is controlled by setting a single property, prefix.defaults.

Properties tend to get confusing and error-prone when trying to express large amounts of configuration data. As such quokka only tends to use them for allowing users to tweak the behaviour of plugins. For large configuration tasks, an XML document with a DTD or schema is a much better solution. Thankfully, quokka supports this too using build resources.

2.2.1. Built-in Properties

Quokka defines the following built-in properties:

q.project.targetDir

The root directory of all generated output. Defaults to ${basedir}/target

q.project.sourceDir

The root directory of all source related files. Defaults to ${basedir}/src

q.project.resourcesDir

The root directory of all project resources. Defaults to ${basedir}/build/resources

q.project.artifact.group

The group of the project artifacts

q.project.artifact.version

The version of the project artifacts

q.project.artifact.name[<type>]

A comma separated list of all artifacts names of the given type in the project.

q.project.path.<pathId>

Each path, fully resolved to files on the local filesystem, separated by the system path separator.

q.preferencesDir

Set to ${user.home}/.quokka on all platforms apart from OS X where it is ${user.home}/Library/Preferences/Quokka.

q.cacheDir

Set to ${user.home}/.quokka/caches on all platforms apart from OS X where it is ${user.home}/Library/Caches/Quokka.

2.3. Build Resources

Build resources provide a mechansim for bundling configuration files together with a plugin or dependency set that can then be overridden by users if required.

The classic use case for build resources is the Jalopy plugin. The jalopy plugin is a Java pretty printer (or source code formatter). As such it is highly configurable and uses a large XML file to specify the configuration data. While it is simple to bundle a configuration file with the plugin, the file bundled will no doubt not match the end user's coding conventions. Mapping the configuration file to properties would be one solution, but it would be tedious and error prone.

The solution is build resources. Build resources are like a global virtual file system, or a big map of files where the files themselves may be bundled inside a plugin .jar file, or bundled inside a dependency set, or placed in the build/resources directory of the project.

Following on with the jalopy example, it defines a build resource of jalopy-style.xml as the location of the configuration file. It then bundles a default configuration inside of the plugin. However, a dependency set may override the configuration by putting a jalopy-style.xml file in its resources directory. This is highly recommended in corporate environments, creating a dependency set once with the corporate standard conventions and then including the dependency set in all projects. Finally, if the file jalopy-style.xml exists the build/resources directory of the project itself, it will be used, overriding any previous definitions.

You can also use build resources to provide files to plugins or Ant tasks that weren't designed to accept them. This is done by specifying a property prefixed with q.project.resource[<build-resource-key>]. When quokka encounters a build resource property, it will extract the resource to the local file system automatically. e.g. q.project.resource[jalopy-style.xml] would result in a path on the local file system that can be used by any task or plugin.

2.4. Versioning

Quokka uses versions in several places, including the artifacts in the repository, quokka itself, and overrides. To be able to compare versions they must be in a standard format. Quokka is using the format defined in the JSR 277 (Java Module System), although it makes a few small changes including adding a repository version to the end and using OSGi range notations (as JSR 277's syntax is incredibly confusing). It also supports free format versions, however, you cannot ever specify such formats in ranges.

Given the increasing popularity of OSGi and the upcoming JSR 277, it makes sense to try and use consistent versions where possible.

The standard format is: major[.minor[.micro[.update]]][-qualifier][~repositoryVersion], where:

major, minor, micro, update

Are all non-negative integers

qualifier

Is a string and can contain alphanumeric characters and "-". Note: unlike JSR 277, "_" is not permitted.

repositoryVersion

A non-negative integer representing the version of the metadata in the repository to use. This is only used in situations where an artifact placed in the global repository is incorrect and needs to be updated. As quokka guarantees that the global repository is immutable, this version is used to distinguish the repository revisions.

Examples of valid versions are: 1, 1.2, 1.0.2.3, 0.1, 2.2~2

When versions are compared the numeric portion is compared as you expect (as if there were padded zeroes when any value is not supplied). The important thing to note that if two versions are otherwise equal, but one has a qualifier and the other doesn't the version with the qualifier is lesser. e.g. 1.0 > 1.0-m01. (This is in accordance with JSR 277, but the opposite to OSGi).

Also note that qualifier are compared via string comparisons, so take care to pad numerics and order qualifiers in a string-wise manner. e.g. mxx for milestones, rcxx for release candidates.

2.4.1. Version Range Unions

Version range unions are used by quokka to specify a range of versions. Quokka uses the mathematical range notation as per OSGi with the addition that multiple ranges can be specified, delimited by a semi-colon.

The mathematical range notation uses a square bracket for an inclusive range and a round bracket for an exclusive range. e.g. [1.2.0, 1.3.0) means version >= 1.2.0 and < 1.3.0. To specify an exact version you need to specify [version, version], not just the version by itself. Version by itself means anything >= version. More examples:

1.4.1

Any version >= 1.4.1

[1.4.1, 1.4.1]

Exactly 1.4.1

[1.4.1, 1.5.0); [1.7.0, 1.7.0]

Any version >= 1.4.1 and < 1.5.0, or version 1.7.0

2.4.2. Snapshots

Snapshots refer to versions of artifacts that are currently under development and are identified by a qualifier suffix of -ss. Snapshots vary over time. That is, a 1.0-ss artifact might be completely different from one week to the next. In contrast, release versions must not vary over time. That is, 1.0 of an artifact must never change from one week to the next.

Snapshots in general are just treated like any other artifact. The only real difference is that snapshots are generally stored in a separate repository to release artifacts.

2.5. Command line options

Type quokka -help for a full list of command line options. The most important options are:

-projecthelp

Prints project help information (major targets available)

-verbose, -v

Be extra verbose (helps see what quokka is doing)

-buildfile, -file, -f <file>

Use the given build file (may be either a build.xml or build-quokka.xml file)

-D<property>=<value>

Sets the given property, overriding any other definitions

-debug, -d

Useful if you suspect quokka has a bug. Prints very detailed diagnostic information, including the stack trace of any exception that occurred.