Working with multi-modules
A "multi-module" project involves more than one subcomponent, with possible dependencies between components. For example, one project may build three separate jar files, and then a single war with all of these jars copied into its WEB-INF/lib folder.
EasyAnt provides strong support for multi-module projects:
- The meta-build build type provides build orchestration (automatically orders the module build according to dependencies).
- Optional module inheritance allows modules in the same project to share configuration settings.
- Optional top-level build script allows extra packaging logic to run at the end of your multi-module build.
The EasyAnt distribution includes a complete two-module example project, found in the [EASYANT_HOME]/example/example-multimodule directory.
Project Setup
Setting up a multi-module project is practically the same as setting up several single-module projects:
- Add a module.ivy to the project root directory to orchestrate the build.
- Set up each submodule in its own subdirectory with its own module.ivy file.
- Declare dependencies on other submodules in each module.ivy as you need them.
Project module.ivy
The project-level module.ivy uses the meta-build build type. This build type implements build orchestration: all submodules are analyzed to determine the required build order, and then each module is built. Simple modules are built first, and then more complex modules that depend on them are built.
Example
For example, you have an application with two modules. One module is the main application, which depends on a core "API" module. You want to build those two projects in the right order. The simplest parent module.ivy file will look like this:
<ivy-module version="2.0">
<info organisation="org.apache.easyant" module="example-multimodule" status="integration">
<ea:build module="meta-build" revision="0.9"/>
</info>
</ivy-module>
Submodule module.ivy
Set up the module.ivy in each submodule as you would any other project; see Getting Started for an overview. Often submodules belonging to the same project will have very similar Ivy files, with common dependencies and build configurations. EasyAnt provides a module inheritance feature that allows you to put features shared by all of your modules in the project Ivy file.
Module inheritance
Module inheritence is based on ivy's extend feature.
<extends organisation="foobar" module="child" revision="latest.integration" location="../module.ivy" extendType="configurations,dependencies"/>
Extend tag has two optional attributes, "location" and "extendType". Location defines the filesystem location of the parent desriptor relative to the submodule descriptor. If the parent descriptor doesn't exist there, Ivy will search for it in the Ivy repository. If unspecified, the default in EasyAnt is "../module.ivy".
The extendType attribute defines which parts of the parent descriptor to import. The default value is "all". Possible values for extendType:
- all
- configurations
- dependencies
- description
Example
<ivy-module version="2.0">
<info organisation="org.apache.easyant" module="example-submodule" status="integration" >
<extends organisation="org.apache.easyant" name="example-parent" revision="latest.integration" />
<ea:build module="build-webapp-java" revision="0.1">
<property name="test.framework" value="testng"/>
</ea:build>
</info>
...
</ivy-module>
In this file we say that "example-submodule" wants to inherit of ALL (configurations/dependencies/ easyant properties /easyant plugins ) that are defined in "example-parent". So our submodule Ivy file defines what is unique about this module, but shares build configurations and property settings common to all modules in the parent project.
Dependencies between submodules
Dependencies between submodules are declared just like any other dependency in your module.ivy file. You will usually want to declare the dependency as revision="latest.integration", so that the latest build is always used.
Building
Building a multi-module project is the same as building a single-module project: type "easyant [target]", where [target] is a highlevel target or target that you want to execute. [target] is then executed on all of your sub-modules, ordered so that dependencies are built before the modules that need them. Type easyant -p from the project root directory to see a full list of available build targets. The following highlevel targets (also named phases) are typical of any easyant build:
> easyant clean
execute the clean phase of the build on each submodule. This deletes any artifacts produced by prior builds.> easyant verify
compile and test all modules> easyant package
package all modules for distribution> easyant publish-local
publish all project modules to your workstation Ivy repository.> easyant publish-shared
publish all project modules to your shared Ivy repository.
Build artifacts
Build reports for each submodule are created in that module's own target directory. During a normal build (like "easyant verify"), artifacts from each submodule will be published into a private "build-scoped" Ivy repository in the project root directory. The default build-scoped repository is in [PROJECT DIR]/target/repository.
When publishing a snapshot / release to a local repo (using publish-local) or shared repo (using publish-shared), the "build" scoped repository is ignored.
Multi-module Packaging
So you have a complex project with many sub-modules, and you use meta-build to generate all of them. At the end of your build you probably want to gather your various modules and put them together into one big distribution archive.
The easiest way to do this is with a module.ant file in the project root directory. Any targets in this file will be run after all of the sub-module builds are finished. For example, this module.ant could be used with our example-multimodule project to build a tar.gz file at the end of the package phase:
<project name="org.apache.easyant#example-multimodule" xmlns:ea="antlib:org.apache.easyant">
<property name="target.dir" location="target/release"/>
<ea:import mrid="org.apache.easyant.buildtypes#meta-build;0.9"/>
<target name="build-distribution" phase="package" description="gather the submodules into one archive">
<mkdir dir="${target.dir}"/>
<tar destfile="${target.dir}/distribution.tar.gz" compression="gzip">
<tarfileset prefix="lib" dir="example-core/target/artifacts" includes="*.jar"/>
<tarfileset prefix="lib" dir="example-hello-world/target/artifacts" includes="*.jar"/>
</tar>
</target>
</project>