Jenkins : Eclipse alternative build setup

This document describes an alternative way of setting up a plugin build environment using Eclipse. It's a bit more technically involved than directly using maven, but it can give faster code-compile-execute cycles, under some circumstances. If it doesn't work, you're probably on your own. If you want a build setup that 'just works', and want to be able to draw on community support if something goes wrong, then you probably want to stick to pure Maven builds.

Table of Contents

Background

Running the compiles using Maven are quite slow on my computer, so I wanted to avoid that, and I wanted to take advantage of the continuous build feature in Eclipse. However, trying to do everything without maven turned out to be fairly masochistic: some things are simplest to leave to maven.

Procedure

Create/download plugin project, and create hpl file

  • use maven to create the new plugin project, or download the existing plugin project from git
  • use maven to create the eclipse project, ie 'mvn eclipse:eclipse'
    • you can now load the project from Eclipse, by using 'import' | 'Existing projects into workspace'

Create hpl file

An 'hpl' file is a text file, written like a manifest file, that goes in the 'plugins' directory of Jenkins home, and which tells Jenkins where to find our, unpackaged, plugin. We need to modify it slightly to point to our eclipse classes, ie 'target/eclipse-classes':

  • use maven to create and install an hpl file, ie 'mvn hpi:hpl -Dhudson_home=/your/jenkins/home/here'
  • open the hpl file, from the 'plugins' subdirectory of jenkins home, in a text editor. Change the first of the paths in the 'Libraries:' entry from 'classes' to 'eclipse-classes'

Configuring jenkins for fast startup

On each build cycle, you will need to restart Jenkins. Therefore, getting Jenkins to start quickly will decrease cycle time:

  • remove all unnecessary optional plugins
  • create 'blahblah.hpl.disabled' files (where 'blahblah' is the name of a plugin), in the plugins directory, for the builtin plugins
  • use 'jar -xf' to extract the jenkins.war file somewhere, let's call this directory $JENKINSAPP
  • launch jenkins by 'java -jar $JENKINSAPP/winstone.jar --webroot=$JENKINSAPP'
    • you probably want to create a batch script for this, to save typing

Now, whenever you make a change, just ctrl-c out of jenkins, and restart it.

Annotation processors

We need to register a couple of annotation processors. These handle creating some meta-data/index files, specifically target/eclipse-classes/META-INF/annotations/hudson.Extension , and some files extending in '.staple', also in target/eclipse-classes. Without these files, certain functionalities will either not exist at runtime, or won't work.

Annotation processor for @Extensions

We need to register the @Extensions from our plugin, which is done using the sezproz annotation processor. As of writing, we need a slightly patched version:

Back in your eclipse project, do 'properties' on your project, and:

  • select 'java compiler'
    • make sure compliance is set to 1.6 or higher
  • go to 'java compiler' | 'annotation processing'
    • click the three checkboxes
  • go to 'java compiler' | 'annotation processing' | 'factory path'
    • tick the 'Enable project specific settings' box
    • click 'add external jars...'
    • go into the 'target' folder of the sezpoz/sezpoz directory, and select the sezpoz SNAPSHOT jar file
    • click 'ok', then 'yes', then 'ok'
  • check that your 'target/eclipse-classes' directory now contains a directory 'META-INF/annotations', and inside it is a file 'hudson.Extension'
    • if that file exists then you're good to go!

Annotation processor for Stapler

We need one more annotation processor to handle the creation of .Staple files, which are necessary for classes containing a @DataBoundConstructor constructor to work correctly.

We need to add stapler-<version number>.jar It depends on commons-io.jar, so we will also need to add that. We need at least version 1.214 of Stapler to use Eclipse. At least, the version 1.172 used by the ec2 plugin didn't work with Eclipse for me.

  • In 'properties' on your project, go to 'java compiler' | 'annotation processing' | 'factory path'
  • click 'add external jars...'
  • go to your .m2/repository directory, and go to the subdirectory org/kohsuke/stapler/stapler
    • pick an appropriate version, at least 1.214
    • change into that directory, and add the jar file (if there's no jar file, try a different version)
  • add also the commons-io.jar file
    • you can get this from your m2 repository, in the directory 'commons-io/commons-io/<version>
    • at least version 1.4 worked ok for me
  • click 'ok', then 'yes', then 'ok'
  • if your target/eclipse-classes directory has some files ending in '.stapler', then this annotation processor is working ok (if there arent any, then perhaps you dont have any @DataBoundConstructor constructor classes?)

Create localizer/.../Messages.java

The resource files 'Messages.properties' need to be converted to the Messages.java file(s), in target/generated-sources/localizer. For now, an easy way of doing this is to use maven, ie 'mvn generate-sources'. You will need to re-run this each time you update any of the .properties files. Actually, running 'mvn eclipse:eclipse' will this for you anyway, though note that it will change the java compliance from '1.6' to '1.5', meaning your annotation processors will stop running until you change this back from project properties.

Register any Plugin extension class

If you have a Plugin extension class, the registration process is as follows:

  • an annotation processor in jenkins-core finds the Plugin derived class, and creates META-INF/services/hudson.Plugin , in your classes output directory, eg target/classes for maven, and target/eclipse-classes for Eclipse
  • then, when you run hpi:hpl, target/classes/META-INF/services/hudson.Plugin gets read, and used to add the 'Plugin-class:' value to your hpl file

Unfortunately, I couldn't find any way of telling hpi:hpl to look in target/eclipse-classes instead of target/classes. Therefore, my current method to register any Plugin class is to run 'mvn compiler:compile' at least once after adding the Plugin extension class, or after moving or renaming it.

Things that can go wrong

My extensions aren't running

That means the sezpoz annotation processor hasn't run correctly. To confirm this is the cause, you can double-check this by doing 'hexdump -C target/eclipse/classes/META-INF/annotations/hudson.Extension'. The file should exist, and should contain the name of your @Extension classes.

I get an error about there being no constructor with no parameters, or no constructor marked @Injectable

That means the stapler annotation processor hasn't run correctly. To confirm this is the cause, check whether there are any .staple files in target/eclipse-classes. If there aren't any, then the stapler processor hasn't run. If there are some, then maybe they're old. Try deleting the target/eclipse-classes directory, doing 'clean' from eclipse, and checking that the .staple files still exist. If they do exist, then the stapler annotation processor is probably running ok. If they dont, then maybe the stapler annotation processor didnt run correctly.

The annotation processors all completely fail to run, ever, on one particular project

Sometimes, the line 'org.eclipse.jdt.core.compiler.processAnnotations=enabled' in .settings/org.eclipse.jdt.core.prefs gets mysteriously deleted. The eclipse interface will continue to show annotations as activated (a bug?). To correct this issue, you can go to 'project properties' | 'Java compiler' | 'annotation processing', then uncheck 'enable annotation processing', then 'apply' then recheck 'enable annotation processing', and 'apply'

The other thing to check is that 'java compliance' in the project properties is set to at least 1.6 (or 1.7), and not 1.5. Note that every time you rerun 'mvn eclipse:eclipse' this will switch back to 1.5.

Plugin class doesn't run

  • first, check if you have a file target/classes/META-INF/services/hudson.Plugin. If you dont, then run 'mvn compiler:compile' once to create it
  • if you have this file, check that your hpl file contains a 'Plugin-Class: ' entry, and that the entry is incorrect. If it doesn't exist, or it's wrong, then rerun 'mvn hpi:hpl' once to add the entry

I get lots of error messages about missing symbols Messages.<something>

You need to generate the target/generated-sources/localizer/.../Messages.java file.

How to debug an annotation processor?

Here's what I did. It might not be the best way, but it does work. If you know a better way, please add your ideas here too.

  • download the appropriate annotation processor source code from github
  • add a bunch of System.out.printlns, especially in the entry points, such as the 'process' function
  • do 'mvn -DskipTests install'
  • close eclipse, and start it from the commandline. This way any System.out.println output will appear in your commandline
  • remove the old annotation processor in eclipse, do 'ok' and 'yes'
  • add the new SNAPSHOT processor that you just compiled, and have a look at the output in your commandline

Note that if you rebuild the annotation processor, you need to first remove the old in eclipse, and do 'ok' and 'yes' for the rebuild, then reopen the project properties, and add the new one. It's not enough to just remove the old one in the properties, and immediately add the new one.