Quokka supports multiple project builds by leveraging the
subant task from Ant. This can be combined with the
built-in buildpath task to automatically discover
sub-projects and calculate a build sequence, taking into account
interdependencies.
Tip
To see a working example of a multi-project build, including
aggregated reports, create a project using the
quokka.archetype.multiproject archetype.
When building multiple projects, there is commonly a parent project that is used to build all of the modules as a single unit. Quokka does not attempt to impose special relationships between parents and children, especially with regard to targets. i.e. it is up to the user to decide what child modules to build and what targets are available on those particular children.
There are a few features to allow parent/child integration:
- parent project reference
-
The parent project reference is available to child projects. This allows children to pass information back to the parent project. For example, the aggregated development reports make use of this to pass back the data for aggregation
- hasParent automatic profile
-
A
hasParentprofile is automatically created by quokka and is enabled for child projects. This enables a child project to enable or disable certain targets when being called by a parent. e.g. when doing aggregated reports such as jUnit and Cobertura it is much faster skipping the report generation at the child project level when only the aggregated report is going to be viewed - buildpath task
-
The
buildpathtask is an in-built Ant task that quokka provides to automatically generate a build sequence from a collection of projects, taking into account the inter-dependencies. It accepts any number of nested resource collections pointing tobuild-quokka.xmland has the following attributes:- id
-
Mandatory. The id of the path to create
- sequence
-
Optional. If true, the projects will be sequenced, otherwise the projects will just be used in the sequence supplied to the Ant task. Default is true.
- cache
-
Optional. The file to cache the calculated build sequence. By default it is
${basedir}/build-sequence/<id>.txt.
Note: Currently the sequencing algorithm cannot handle the case where a project depends on a dependency set that not in the repository, but is due to be built as part of the sequence. A work-around is to install the dependency set once in isolation before starting the sequencing.
Apart from facilitating the building of multiple projects as a single unit, the other major use of parent projects is to aggregate reports such as unit testing and code coverage to get an overall picture of the project. The jUnit, Cobertura, JavaDoc and Help plugins all support aggregation.
The following is an example of a multiple project build taken from
the quokka.archetype.multiproject archetype. The
archetype consists of the following modules:
-
depset: A dependency set that factors out the common plugins and dependencies to all code modules.
-
util: A java utilities module, depends on depset
-
server: A java server module, depends on depset, util
-
client: A java client module, depends on depset, util, server
Below is the parent build.xml used to build the
modules:
<project name="master">
<!-- Code modules in sequence allowing for inter-dependencies -->
<path id="buildpath-code" path="modules/util:modules/server:modules/client"/>
<!-- All modules -->
<path id="buildpath">
<path path="modules/depset"/>
<path refid="buildpath-code"/>
</path>
<!-- A macro to run a target on all child modules and optionally the master -->
<macrodef name="iterate">
<attribute name="target"/>
<attribute name="master" default="none"/>
<attribute name="buildpathref" default="buildpath"/>
<sequential>
<subant target="@{target}" buildpathref="@{buildpathref}"
antfile="build-quokka.xml"/>
<if>
<not><equals arg1="@{master}" arg2="none"/></not>
<then>
<run-target target="@{master}"/>
</then>
</if>
</sequential>
</macrodef>
<target name="install-all" description="Install all modules">
<iterate target="install"/>
</target>
<target name="clean-all" description="Cleans all modules">
<iterate target="clean" master="clean"/>
</target>
<!-- This is an example of building an individual report for all modules in isolation
Note: By using the collect target on the child modules, it skips the
report generation at lower levels, only collecting the data -->
<target name="cobertura-all" description="Runs the cobertura report for all modules">
<iterate target="cobertura:collect" master="cobertura:aggregate-report"
buildpathref="buildpath-code"/>
</target>
<target name="reports-all"
description="Runs the dev reports for all modules and aggregates results">
<iterate target="devreport:generate" master="devreport:reports"/>
</target>
</project>
As you can see, the above file is as standard Ant build file, using mostly standard Ant tasks. This provides a great deal of flexibility for the interaction between parents and children. For example, multiple build paths are defined, one for all modules and one for code modules, allowing subsets of modules to be handled differently.
The above example does use several bundled tasks from the
ant-contrib library, notably the
if and run-target tasks.
run-target is important as it allows the build file
to execute targets on the same project instance (unlike
ant-call). This allows it to find report data stored
on the parent instance by child modules. See Built-in Ant Extensions for more
information on what additional built-in tasks are available.
To complete the picture, here is the parent
build-quokka.xml file:
...
<project name="master" default-target="install-all">
<dependency-set>
<!-- Clean target directory -->
<plugin group="quokka.plugin.standard-lifecycle" version="0.3"
use-defaults="false" targets="clean"/>
<!-- Aggregated development reports -->
<plugin group="quokka.plugin.devreport" version="0.3"/>
<plugin group="quokka.plugin.cobertura" version="0.3"
use-defaults="false" targets="aggregate-report"/>
<plugin group="quokka.plugin.junitreport" version="0.3"
use-defaults="false" targets="aggregate-report"/>
<plugin group="quokka.plugin.help" version="0.3"
use-defaults="false" targets="aggregate-report, help"/>
<plugin group="quokka.plugin.devreport" version="0.3"
use-defaults="false" targets="reports"/>
</dependency-set>
</project>
It includes the clean plugin and various
aggregated reports.