Alex Blewitt
will be presenting a short talk on Thursday our very own RCPApps.org at
EclipseCon. Feel free to stop by and say hi. As well as the short
talk, there's also a more detailed tutorial on Automated Builds of Rich Client Applications
which wil be happening on the Monday.
The automated build example uses RCP News as the example, and it can be downloaded from the EclipseCon website, or checked out from the CVS repository at SourceForge.
:pserver:anonymous@rcpapps.cvs.sourceforge.net/cvsroot/rcpappsscm:cvs:pserver:anonymous@rcpapps.cvs.sourceforge.net:/cvsroot/rcpappsHere's what software you're going to need to follow through the tutorial:
The RCP News source code
can be checked out of the CVS
repository, using either the STABLE or ECLIPSE_CON_2006 tags.
(You can also live on the bleeding edge and get the HEAD as well, but
it's not guaranteed to work.) Both the STABLE and ECLIPSE_CON_2006 tags
are fluid; so if there are any updates, a cvs update -P
will bring up the newest version.
There's a bundle available at SourceForge if you're behind a firewall and can't check out from anonymous CVS that you can use as well. This has CVS information with the anonymous tags, and so can be used as an initial download and then updated after download.
Given how often it's needed to run a build.xml
tool via the Eclipse antrunner tool,
I put together a simple shell script and cmd file
that can be put on the path for simple access. It also sets the
eclipse.home property to that of the Eclipse install,
if any of the Ant tasks require that information.
There's also an example Cruise Control configuration file that can be used if building the RCPNews product via CruiseControl.
If you want to follow all of the EclipseCon tutorial, you need to have:
Eclipse 3.1.x SDK. You'll need one of the 3.1 builds of the Eclipse SDK. Get Eclipse 3.1.2 SDK here
A command line Zip utility. This is used by the Eclipse release engineering process to zip the bundle up into a file. They use zip because it preserves executable bits, rather than Jar which would be on the classpath.
If you can't live with the build getting to this stage and then failing (instead of saying BuildSucceeded) then download a version such as InfoZip and put it on the path.
CVS on the path. Even if you've got the latest release
checked out, the Eclipse release engineering process needs to talk
back to a CVS server to get a specific version, and needs CVS
to do the work (other repositories don't work at present). Plus,
since it needs to do this behind the scenes, you've got to have
a password-less way of accessing the files (either by doing an
anonymous connection, or by doing an SSH key access
over an :ext: connection.
If you're doing this on a non-networked machine, you'll need to check the source code into your own local copy of CVS so that the release build can talk to a server to load the files.
Windows has a free client at CVSNT.org, although they don't have a server included.
Maven 1.0.2. This is the current stable release of Maven,
and although 1.1 is in beta, it's not clear if it's going to make
it out any time soon. (Maven 2.0 is one step forwards, half a step
sideways, and three and a quarters turn to the left. One of the
biggest problems is that the maven.xml file has been
removed, so unless there's a Maven plugin that does exactly what
you want, you're buggered.) Get Maven 1.0.2 here.
Cruise Control. An excellent choice for automated build daemons. Get CruiseControl 2.4.0 here.
Continuum. An simple but effective choice for automated build daemons. Get Continuum 1.0.2 here.
It's possible to build the application using the Eclipse release engineering process with a standard Eclipse SDK. The first step is to obtain the release engineering files that will kick off the build:
cvs -d :pserver:anonymous@rcpapps.cvs.sourceforge.net:/cvsroot/rcpapps checkout org.rcpapps.base.feature/releng
Once that is downloaded, it's possible to run ant on the build.xml file. However, rather than running it with a vanilla install of ant, you need to run it with the Eclipse antRunner. You might find it easier to set up a script -- such as eant mentioned above -- but the net effect is to run a command like:
java -classpath ${ECLIPSE_HOME}/startup.jar org.eclipse.core.launcher.Main
-application org.eclipse.ant.core.antRunner
-Declipse.home=${ECLIPSE_HOME}
-Dpde.build.scripts=${ECLIPSE_HOME}/plugins/org.eclipse.pde.build_3.1.x/scripts
Note: the build.xml file inside the releng directory specifically refers to the 3.1.0 version of org.eclipse.pde.build plugin. If you have a 3.1.2 install, you'll either need to update the build.xml file, or specify -Dpde.build.scripts=${eclipse.home}/plugins/org.eclipse.pde.build_3.1.2/scripts. A failure to specify the correct version here will result in ant errors and being unable to find build.xml files during various targets.
There's actually two features that can be built in the example. The first is the org.rcpapps.base.feature, which just contains a very simple UI and key features that can be shared. There's no application in this, but if you want to experiment with the build process it's faster than downloading the entire build each time. The other is the org.rcpapps.rcpnews.feature, which allows you to build the entire system.
This build connects to the remote CVS server to download the appropriate version of the code (as specified in the map files). As a result, if you're downloading from the CVS server, you'll need to be connected to the 'net in order to do this. If you're not connected to the 'net, you've got a couple of options:
Check it into a local CVS server, and override the -DcvsRoot= on the command line properties.
Copy the files into Build/features/ and Build/plugins/, and run with -DskipFetch=true. This approach is also useful if you don't want to use CVS to store your files for an automated build.
If the build is successful, you should have Build/org.rcpapps.base.feature-STABLE/RCPBse-STABLE.zip which you can unzip and install into an Eclipse install.
Update: by default, this downloads from the CVS repository using the STABLE tag. Unfortunately, some set of files were not tagged properly in the CVS server which means this may fail with an error regarding "getLocations() not found". Although the code is in HEAD and has been re-tagged, the CVS server at SourceForge takes anywhere up to a day to resync. In the meantime, you can change which version of the codebase you use with -DcvsVersion=HEAD on the eant command line.
It's also possible to build RCPNews with Maven. Obviously, you'll need maven installed; but since maven downloads components on demand (generally, the first time that they're used) you'll need to be on-line the first time that you run the maven build process. Thereafter, it will cache the maven code in your local system and you can run the builds off-line.
You'll need to downlaod the entire source tree, since maven operates on an existing codebase. It's also possible to get maven to udpate from a repository once it's downloaded initially, but it's not often used to bootstrap itself. Maven is configured with a maven.xml file, which has been set up for the features and plugins.
The easiest way of obtaining the entire tree is to check out the RCPNews module:
cvs -d :pserver:anonymous@rcpapps.cvs.sourceforge.net:/cvsroot/rcpapps checkout RCPNews
Once it's downloaded, you need to install the Eclipse runtime libraries into the ${MAVEN_REPO} for the build to find. By default, it is installed into ~/.maven/repository, but it can be accessed from anywhere the ${MAVEN_REPO} points to. The Eclipse libraries need to be installed into ${MAVEN_REPO}/eclipse/jars, and each needs to be a Jar of the form org.eclipse.plugin-3.1.0.jar. You'll also need to put xom-1.1.jar into ${MAVEN_REPO}/nu.xom/jars/xom-1.1.0.jar.
Given that most Eclipse plugins are now Jars, this installation is relatively easy. You can either copy all of the Jar files, or use symlinks if your operating system can handle them. Unfortunately, whilst Eclipse jars are use org.eclipse.plugin_1.2.3.jar as a format, maven jars use org.eclipse.plugin-1.2.3.jar as a format. So you'll need to rename each on the fly, using something like AWK to re-write _ as - in the names (or do it manually).
Of course, nothing in life is easy and this applies to setting up the Eclipse jars in the maven repository. You'll find that there are two other issues that must be dealt with; unpacked plugins (like org.eclipse.core.runtime) need to be handled separately, as do fragments. Generally, unpacked plugins have one (or more) jars internally, and you need to condense all the jars into a single one for installation into the maven repository. As for fragments, you have to merge them into one Jar for the maven repository to compile against it. The one that will cause problems is the SWT plugins, since there's no code in the main plugin; instead, all of the code is in the fragment. A simple way of doing this is to install org.eclipse.swt.win32.win32.x86_3.1.0.jar as org.eclipse.swt-3.1.0.jar into the ${MAVEN_REPO}.
Note: the RCPNews code references specify plugin dependencies as 3.1.0. Primarily, this is because you have to rename the plugins anyway; so having to rename a plugin like org.eclipse.core.runtime_3.1.2.jar as org.eclipse.core.runtime-3.1.0.jar isn't much of a problem. The other alternative would be to have 3.1.jar in the maven files, and then install any version of Eclipse 3.1.x into a maven repository.
Having configured the ${MAVEN_REPO} with the required jars, running maven is actually pretty easy. All you need to do is to run maven in the org.rcpapps.base plugin, and it will build, test and then jar/install the plugin. You can then build the others in the following order:
org.rcpapps.base
org.rcpapps.base.ui
org.rcpapps.base.feature
org.rcpapps.rcpnews
org.rcpapps.rcpnews.ui
org.rcpapps.rcpnews.help
org.rcpapps.rcpnews.application
org.rcpapps.rcpnews.feature
If you've set ${ECLIPSE_HOME}, you can also run maven eclipse:install to install it into an Eclipse runtime environment.
But one of Maven's strengths is that you don't need to worry about the dependencies. You can get it to build all subprojects of a folder with the maven multiproject command. Change up to the directory that contains everything, and run:
maven multiproject:install
This will run the default goal for each project (in this case, a build) and then generate the appropriate jars for each of the plugins. It's also possible to specify non-default goals:
maven -Dgoal=eclipse.install multiproject:goal
You can see the individual generated jars in the target/ directory of each of the plugins and features. You could then (for example) generate an update site or a complete zip file.
Cruise control is configured by a config.xml which defines where the build file is. It's capable of understanding both ant and maven scripts. If you're using ant, you need to ensure that you specify the antscript property so that you can run the eant to ensure it runs within Eclispe. The config file will look like:
<cruisecontrol>
<project name="RCPNews">
<listeners>
<currentbuildstatuslistener file="logs/${project.name}/status.txt"/>
</listeners>
<bootstrappers>
<cvsbootstrapper localWorkingCopy="projects/${project.name}" />
</bootstrappers>
<modificationset quietperiod="30">
<cvs localWorkingCopy="projects/${project.name}"/>
</modificationset>
<schedule interval="300">
<ant usedebug="false" uselogger="true"
antscript="projects/${project.name}/eant"
buildfile="projects/${project.name}/org.rcpapps.rcpnews.feature/releng/build.xml"/>
</schedule>
<log>
<merge dir="projects/${project.name}/target/test-results"/>
</log>
<publishers>
<onsuccess>
<artifactspublisher dest="artifacts/${project.name}"
file="projects/${project.name}/org.rcpapps.rcpnews.feature/releng/
Build/org.rcpapps.rcpnews.feature-STABLE/RCPNews-STABLE.zip"/>
</onsuccess>
</publishers>
</project>
</cruisecontrol>
After starting cruisecontrol (with bin/cruisecontrol.sh) you can view the process at http://localhost:8080. You can then kick off a build, and watch the results come in.
Continuum is easy to set up with either ant or maven. You don't need to explicitly set anything up; rather, entries are added by the web interface (which you can view at http://localhost:8080/continuum/. You need to run bin/plexus.sh to fire it up.
If you're using an ant script, you have to set up a shell executable to fire off the ant process. The shell must be something like /bin/sh, and the argument is the eant that you've defined earlier.
Maven supports automated testing, as does the releng build. Maven is well suited for running quick, out-of-container tests whereas the releng process is well suited for running in-container tests. The only caveat for in-container testing is that the test code must be deployed as part of the plugin as well in order for it to be callable by the eclipse testing process.
Tests are best grouped into an AllTests suite. This allows many tests to be run in a single container instance without having to restart it for each test. The RCPNews uses a dynamic AllTests mechanism that searches for all classes ending in Test. To test it, you need to call the test framework, available in the org.eclipse.test_3.1.0 plugin, which is available for separate download if you haven't got it already.
Tests are available in ui-test or core-test flavours; the former starts up a UI interface, whereas the latter runs headless. Both are provided as separate application hooks and the library.xml essentially provides a mechanism to call these applications with a plugin and test name. The net effect is a command that looks like:
java -classpath ${ECLIPSE_HOME}/startup.jar
-Dformatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJunitResultFormatter,${PLUGIN}.xml
org.eclipse.core.launcher.Main
-application org.eclipse.test.coretestapplication
-className ${PLUGIN}.AllTests
-testPluginName ${PLUGIN}
The results can be collected with a <junitreport> to present a nice HTML page summary. Alternatively, the build tools (maven, cruisecontrol) may do the collection and formatting automatically.
Let me know what you think! If you'd like to discuss the application or the build process, drop comments to the RCPApps blog, or drop me a mail to my GMail address (alex.blewitt).