Automated Build of RCP Artifacts with Maven Tycho - A field report (Part 1)

This post series should not be a tutorial for Maven Tycho. You can find many tutorials on the web (see for example [2] and [3]). This post series should be a field report about the problems during the introduction of Maven Tycho.

Why Maven Tycho?

One requirement is that we can use Maven artifacts. So it seems obvious to search after a solution based on Maven. After a search on the web, two Maven plugins are noticed:

  • PDE Maven Plugin ([7]): This plugin builds the RCP artifacts with the help of a generated Ant script by Eclipse. But this generated Ant script is error-prone.
  • Maven Tycho Plugin ([1]): Very young plugin to build RCP artifacts (current version 0.13.0), but the community is very active. It tries to build RCP artifacts in the similar way than Eclipse.

The Proceeding

During working the tutorials out the impression occurs that the main problem is to solve the dependency problem. So this is the first task for the introduction of Maven Tycho.

Solution of the dependency problem

In Maven the dependencies are declared in the POM file. In Eclipse the dependencies are declared by a target definition file. The goal is to merge these both worlds. Maven Tycho enables Maven to build against a pre-defined target definition file.

The next question is where the dependencies come from. Maven's solution is the Maven Repository. Eclipse has its own solution, P2 repository. In Eclipse's target definition file you can specify a URL to a P2 repository and Eclipse searches after the needed dependencies, automatically.

A further requirement is that the Maven repository can still use. The reason is that our server artifacts are stored in a Maven repository and the communication between client and server is via Hessian, so some artifacts are needed by client and server. Furthermore, many third party libraries are only available in a Maven repository.

All examples in the tutorial work only with P2 repositories. But the introducing of a P2 repository is not an option, because then two repositories have to be administered.

First Approach - Define a Target Definition File with the Type 'Directory'

The idea of this approach is that you can define a folder with jars (that are the dependencies) in the target definition file. So it seems that the problem can be reduced to how I get the dependencies to this folder.

This problem can be solved by the Maven Dependency Plugin ([9]). It has the goal copy-dependencies. This goal copies all dependencies that are declared in the <dependencies>-element of the pom file to a declared output directory. The configuration of the dependency plugin could look like the following one:

 1<?xml version="1.0" encoding="UTF-8"?>
 2<plugin>
 3  <groupId>org.apache.maven.plugins</groupId>
 4  <artifactId>maven-dependency-plugin</artifactId>
 5  <version>2.3</version>
 6  <executions>
 7    <execution>
 8      <id>copy-dependencies</id>
 9      <phase>process-resources</phase>
10      <goals>
11        <goal>copy-dependencies</goal>
12      </goals>
13      <configuration>
14        <excludeTransitive>true</excludeTransitive>
15        <outputDirectory>${project.build.directory}/plugins</outputDirectory>
16      </configuration>
17    </execution>
18  </executions>
19</plugin>

This solution works well in Eclipse, but not in with Maven's mvn clean install, because Maven Tycho doesn't support target type 'Directory', only the target type 'Software Site'. So this solution doesn't work for us.

Second Approach - A solution based on Maven's 'pom-first' approach

With Maven Tycho, you can define that the RCP artifacts should be built against the dependencies that are defined in the POM (old-known Maven way).

1<?xml version="1.0" encoding="UTF-8"?>
2<plugin>
3  <groupId>org.eclipse.tycho</groupId>
4  <artifactId>target-platform-configuration</artifactId>
5  <version>0.13.0</version>
6  <configuration>
7    <pomDependencies>consider</pomDependencies>
8  </configuration>
9</plugin>

With Maven Tycho, the build works fine. But in Eclipse you cannot work in Eclipse-known way, because it has not the needed dependencies. I thought I could solve this problem with the Maven Eclipse Plugin and its goal eclipse:eclipse. This plugin generates all needed Eclipse files but the dependencies are generated in the Java Build Path named Referenced Library. But Eclipse needs the dependencies for the RCP plugin development in the Java Build Path named Plug-in Dependencies. Next try!

Third Approach - Nexus P2 Repository Plugin

A further research results that Nexus OSS should get a new plugin that can simulate the access to a Maven repository whether it would be a P2 repository (P2 proxy) (see [4]). This plugin is an experimental feature at the moment, so you can only use the SNAPSHOT version of this plugin. Unfortunately, the SNAPSHOT version (mid December 2011) that I used did not work with Nexus OSS version 1.9.2.2. But I think this would be a good idea for this problem.

Fourth Approach - Generation of a Local P2 Repository

A hint in the Tycho user mailing list (see [5]) helps me find this approach. It exits a Maven Tycho plugin (see [6]), that can generate a P2 repository from a directory with a bunch of bundles. In the first approach, we used the Maven Dependency Plugin to copy all needed dependencies in a directory. For this approach I combine these two plugins with two another plugins to build a local P2 repository.

By the Maven Dependency Plugin all dependencies are copied to a directory named plugins. The Tycho P2 Extra Plugin can generate from this directory a local P2 repository. Unfortunately, this plugin does not generate the needed feature.jar. But without this jar Eclipse does not recognize that we have here a P2 repository (an introduction to the P2 repository you can find in [10]). So we have to build this jar.

We create a new Maven project with the packaging option jar. In this project, there is only one file: feature.xml. In this xml file the name of the bundles of the plugins directory are stored. The next code snippet shows an example for this entry:

1<?xml version="1.0" encoding="UTF-8"?>
2<plugin  id="org.eclipse.core.contenttype"  download-size="0"  install-size="0"  version="3.4.100.v20110423-0524"  unpack="false"/>

Then this new generated feature.jar are copied by the Maven Dependency Plugin to the directory features. Now with these two directories (features and plugins), the Tycho P2 Extra Plugin can generate a local P2 repository that are recognized by Eclipse.

The next problem is that the target definition file accepts only absolute path for the target type 'Software Site'. But we want a target definition file that is independent of the platform. So we save a target definition file with a token for the location of the local P2 repository in src/main/resources. During the Maven build this token are replaced by absolute platform-dependent path to the location of the local P2 repository. This replacement is done by the Maven Replacer Plugin ([11]). The next code snippet shows the whole plugin configuration for the above described solution:

  1<?xml version="1.0" encoding="UTF-8"?>
  2<plugin>
  3  <groupId>org.apache.maven.plugins</groupId>
  4  <artifactId>maven-dependency-plugin</artifactId>
  5  <version>2.3</version>
  6  <executions>
  7    <execution>
  8      <id>copy-dependencies</id>
  9      <phase>process-resources</phase>
 10      <goals>
 11        <goal>copy-dependencies</goal>
 12      </goals>
 13      <configuration>
 14        <excludeTransitive>true</excludeTransitive>
 15        <outputDirectory>${project.build.directory}/source/plugins</outputDirectory>
 16      </configuration>
 17    </execution>
 18    <execution>
 19      <id>copy-feature</id>
 20      <phase>process-resources</phase>
 21      <goals>
 22        <goal>copy</goal>
 23      </goals>
 24      <configuration>
 25        <artifactItems>
 26          <artifactItem>
 27            <groupId>skm.p2</groupId>
 28            <artifactId>repository.feature</artifactId>
 29            <version>0.0.1-SNAPSHOT</version>
 30            <type>jar</type>
 31            <overWrite>true</overWrite>
 32            <outputDirectory>${project.build.directory}/source/features</outputDirectory>
 33          </artifactItem>
 34        </artifactItems>
 35      </configuration>
 36    </execution>
 37  </executions>
 38</plugin>
 39<plugin>
 40  <groupId>org.apache.maven.plugins</groupId>
 41  <artifactId>maven-resources-plugin</artifactId>
 42  <version>2.5</version>
 43  <executions>
 44    <execution>
 45      <id>copy-target-definition</id>
 46      <phase>process-resources</phase>
 47      <goals>
 48        <goal>copy-resources</goal>
 49      </goals>
 50      <configuration>
 51        <resources>
 52          <resource>
 53            <directory>src/main/resources</directory>
 54            <includes>
 55              <include>${target.definition}</include>
 56            </includes>
 57          </resource>
 58        </resources>
 59        <outputDirectory>${project.build.directory}</outputDirectory>
 60      </configuration>
 61    </execution>
 62  </executions>
 63</plugin>
 64<plugin>
 65  <groupId>com.google.code.maven-replacer-plugin</groupId>
 66  <artifactId>maven-replacer-plugin</artifactId>
 67  <version>1.4.0</version>
 68  <executions>
 69    <execution>
 70      <phase>prepare-package</phase>
 71      <goals>
 72        <goal>replace</goal>
 73      </goals>
 74    </execution>
 75  </executions>
 76  <configuration>
 77    <ignoreMissingFile>false</ignoreMissingFile>
 78    <file>target/${target.definition}</file>
 79    <regex>false</regex>
 80    <replacements>
 81      <replacement>
 82        <token>$repository.location$</token>
 83        <value>${project.build.directory}/repository/</value>
 84      </replacement>
 85      <replacement>
 86        <token>
 87        </token>
 88        <value>/</value>
 89      </replacement>
 90    </replacements>
 91  </configuration>
 92</plugin>
 93<plugin>
 94  <groupId>org.eclipse.tycho.extras</groupId>
 95  <artifactId>tycho-p2-extras-plugin</artifactId>
 96  <version>0.13.0</version>
 97  <executions>
 98    <execution>
 99      <phase>prepare-package</phase>
100      <goals>
101        <goal>publish-features-and-bundles</goal>
102      </goals>
103    </execution>
104  </executions>
105  <configuration>
106    <compress>false</compress>
107  </configuration>
108</plugin>
109<plugin>
110  <groupId>org.eclipse.tycho</groupId>
111  <artifactId>tycho-p2-repository-plugin</artifactId>
112  <version>0.13.0</version>
113  <executions>
114    <execution>
115      <phase>package</phase>
116      <goals>
117        <goal>archive-repository</goal>
118      </goals>
119    </execution>
120  </executions>
121</plugin>
122<plugin>
123  <groupId>org.codehaus.mojo</groupId>
124  <artifactId>build-helper-maven-plugin</artifactId>
125  <version>1.7</version>
126  <executions>
127    <execution>
128      <id>attach-artifacts</id>
129      <phase>package</phase>
130      <goals>
131        <goal>attach-artifact</goal>
132      </goals>
133      <configuration>
134        <artifacts>
135          <artifact>
136            <file>target/${target.definition}</file>
137            <type>target</type>
138            <classifier>indigo</classifier>
139          </artifact>
140        </artifacts>
141      </configuration>
142    </execution>
143  </executions>
144</plugin>

In Eclipse, this generated target definition file works fine, but in Maven Tycho it does not work: Before Maven Tycho builds the local P2 repository and the target definition file, it tries to build the RCP plugins against the not yet existing target definition file and the build failed.

Fifth Approach and Final Solution - Combination of Dependency in the POM and Target Definition

We know Maven works with in the POM defined dependencies, perfectly and Eclipse works with the target platform, perfectly. So the idea is to combine this two approaches, so that every tool can work in its perfect way. So I combine the first both approaches that are described in the above chapters.

All dependencies that are used by all RCP plugins are defined in the root project POM, so that every sub module that defines one RCP plugin is built against these dependencies and one sub module is for the building of the target definition file (this build is described in the first approach). Maven Tycho builds the RCP artifacts in the known Maven way and in Eclipse the RCP artifacts are developed with the generated target definition file.

[1] Maven Tycho Home Page
[2] Tutorial by Matthias Holmqvist - Good for the first introduction
[3] Tutorial with example (in German)
[4] Nexus P2 Repository Plugin (Experimental Feature)
[5] Post on Tycho User Mailing List - How to build local P2 Repository
[6] Introduction to the Tycho-P2-Extra-Plugin
[7] PDE Maven Plugin
[8] Hessian Protocol
[9] Maven Dependency Plugin
[10] Introduction to the format of P2 repositoy
[11] Maven Replacer Plugin