Wednesday, February 02, 2011

Embedding the Orion Editor

[Edit Jan 3, 2012: Orion has evolved significantly since this post was first written a year ago. Please see this post written by Felipe for an update.]

Yesterday I wrote a blog post which contains a few snippets of Ant code. If you have java-script enabled, those snippets should be nicely formatted with line numbers and some basic syntax highlighting.

Those code snippets are actually being shown using the Orion Editor embedded into my blog post. Granted, using the full editor here is a little over-kill since we aren't doing very much with it, but this was an interesting exercise.

Here is the javascript I wrote to do this:

 * Copyright (c) 2011 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
function findOrionElements(tagName){
   var elements = [];
   var tags=document.getElementsByTagName(tagName);
   for(var i=0;i<tags.length;i++) {
      if(tags[i].getAttribute('name')==='orion') {
   return elements;

function createEditors() {
   //find all pre elements named 'orion'
   var elements = findOrionElements('pre');

   for(var i=0; i < elements.length; i++) {
      var element = elements[i];

      //extract the text from inside the pre
      var text = "";
      for( var j=0; j < element.childNodes.length; j++ ) {
         var nodeName = element.childNodes[j].nodeName;
         if (nodeName === "#text")
            text += element.childNodes[j].nodeValue;
         else if (nodeName === "BR" )
          text += '\n';
      /* Create the editor: 
          - parent is the containing div element
          - readonly by default, but can specify class="writable"
          - use the given stylesheet  */
      var parentNode = element.parentNode;      
      var elementClass = element.getAttribute('class');
      var editor = new eclipse.Editor({
         parent: parentNode, 
         readonly: !(elementClass && elementClass.indexOf('writable') > -1),
         stylesheet: ""

      // use javascript styler for now, there is no html/xml syntax highlighting yet
      var styler = new eclipse.TextStyler(editor, "js");
      // add a ruler with line numbers to the left side
      var lines = new eclipse.LineNumberRuler("left", {styleClass: "ruler_lines"}, {styleClass: "ruler_lines_odd"}, {styleClass: "ruler_lines_even"});

      //fix the height of the containing div = (editor.getLineHeight() * (editor.getModel().getLineCount() + 1)) + 2 + 'px';  

This code is looking for all <pre name='orion'> elements and creating editors to replace them. These elements should generally be contained inside a <div> element. HTML for a replaceable section would look like this:

<div><pre name="orion" class="writable">
   var hello = "Hello World!";

This example above was made writable by adding "class='writable'" to the <pre/> element. Inside the <pre> element, all '<' should be escaped as '&lt;' and all '>' as '&gt;'.

As a final step, to get this working on, I combined my javascript together with:
and minified it all using the Google Closure compiler. I hosted the resulting .js file on and added the following lines to the bottom of my blog post:

<script type="text/javascript" src=""></script>
<script type="text/javascript">document.onload=createEditors();</script>

If someone visits the post without javascript enabled, then they don't get the nice formatting on the code snippets, but at least they are still inside <pre> elements which give them some basic formatting.

Tuesday, February 01, 2011

Overriding PDE/Build with the Ant Import task

PDE/Build has a number of places where you can run custom ant scripts during the build.

The general pattern is the you would copy one of the customization templates from into your builder directory and then modify the file as required. For simple builds it is often unnecessary to copy these files at all and PDE/Build will just use its original copy.

If you only have a small change to make to the customization scripts, then it can be cleaner to not copy the template file and instead use Ant's Import task.

Using Import to make minor changes

The ant Import task allows for overriding a target from the imported file. As an example, the Eclipse SDK includes the Build Id in its about box. The About Box contents come from "about.mappings" files inside plug-ins.

What we want to do is after getting all our source from CVS, we do a quick replace in all the about.mappings files to update them with the build id.

Instead of copying the customTargets.xml file into our builder, we create our own that contains just the following:
<project name="customTargets overrides" >
<import file="${eclipse.pdebuild.templates}/headless-build/customTargets.xml"/>

<target name="postFetch">
<replace dir="${buildDirectory}/plugins" value="${buildLabel}" token="@build@">
<include name="**/about.mappings" />

${eclipse.pdebuild.templates} is a property that is automatically set by PDE/Build and it points the folder containing the template files. This small snippet is much cleaner than copying the entire customTargets.xml just to add a few lines.

This pattern can make for smaller and neater build scripts, but it turns out that this can also be a very powerful tool for modifying PDE/Build's behaviour.

Here is the Magic

During M5 milestone week, the Orion builds began failing about 90% of the time. The failure was "Unable to delete directory" in the middle of packaging ant scripts that are generated by PDE/Build. This seems to have been caused by an overloaded/lagged NFS server (or disk array).

When building a product for multiple platforms with p2, PDE/Build installs the product into a temporary directory, zips it up, deletes that directory, and then repeats for the next platform using the same temporary directory. If there is a problem deleting the directory then we are in trouble because even if we could ignore the problem, the next platform will be contaminated with contents from the previous one.

In order to work around the problem, we need to modify a target named cleanup.assembly which simply performs an ant <delete/> on the temporary directory. The problem is, that this target is in the middle of the PDE/Build generated configuration specific packaging scripts. A deeper understanding of how these scripts work is required.

Package Script Overview

When running a product build, the generated package scripts are organized per platform that we are building for. As an example, if we are building for windows, mac and linux, then we would have the following scripts:
The "" portion of the file name comes from this being a product build. In a feature build this would be the name of the top level feature being built. The first (*.all.xml) script is the main entry point for the packaging process. Each of the other scripts do the packaging for each platform. Every one of those platform specific scripts contain a cleanup.assembly target that needs to be modified.

Script Delegation

The top level packaging scripts does not call all the others directly, rather it uses a kind of delegation through the allElements.xml file. This file can be copied to your builder and modified to change the archive name or perform pre or post processing on the archive.

For each platform, the top level packaging script will call allElements.xml/defaultAssemble (or a platform specific assemble.*.[config] target if one is defined) passing it the name of the platform specific packaging script to invoke.

This is where we can insert our change in order to override the platform specific packaging scripts.

The Modified allElements.xml file

We copy the allElements.xml file from into our builder and change the "defaultAssemble" target to look like this:
<target name="defaultAssemble">
<ant antfile="${builder}/packageOverride.xml" dir="${buildDirectory}">
<property name="assembleScriptName" value="${assembleScriptName}" />
<property name="archiveName" value="${archiveNamePrefix}-${config}.zip"/>

The name of the platform specific packaging script is specified by the ${assembleScriptName} property. Instead of calling this directly, we instead call a script of our own "packageOverride.xml" and pass it the script name. Product builds normally use their own allElements.xml provided by PDE/Build which also sets the archive name based on the configuration being built. Since we will be using our own allElements.xml file, we also set the archive name here.

Product Builds (using are hardcoded to use their own copy of the allElements.xml file. In order to change this we must set a property allElementsFile which points to our copy. This property must be set before invoking productBuild.xml, which means setting it on the command line, or in a wrapping ant script. This is not necessary when doing a feature build.

The new packageOverride.xml script

The allElements.xml delegation script has now been modified to invoke our own packageOverride.xml script. Our script looks something like this:
<project name="package.override" default="main" >
<import file="${buildDirectory}/${assembleScriptName}" />

<target name="cleanup.assembly">
<condition property="doAssemblyCleanup" >
<not><isset property="runPackager" /></not>
<contains string="${assembleScriptName}" substring="package." />
<antcall target="perform.cleanup.assembly" />
<target name="perform.cleanup.assembly" if="doAssemblyCleanup" >
<exec executable="mv" dir="${buildDirectory}" >
<arg value="${assemblyTempDir}" />
<arg value="${buildDirectory}/tmp.${os}.${ws}.${arch}" />
<exec executable="rm" dir="${buildDirectory}" >
<arg line="-rf ${buildDirectory}/tmp.${os}.${ws}.${arch}" />

Here, we import the package script that was passed to us from allElements.xml, each time the packaging script calls us, we will be importing a different script. We inherit all the generated targets and override the cleanup.assembly target. Our modified version moves the temporary folder to a different location before trying to delete it. If the delete fails, that is ok because it is no longer in the way of the next platform. I used the native 'mv' and 'rm' hoping that they would behave better with a slow NFS server.

The packageOverride.xml script must specify default="main" as that setting is not inherited.

It is important to note that this override also affects the generated assemble.* scripts which are very similar to the package scripts. The assemble scripts also have an cleanup.assembly target which is getting overridden here. However, that target is only supposed to run during assembly if we are not going to be doing packaging. This is why we need a condition here to make sure the temporary folder is only deleted when it should be. The condition I used here would be wrong for feature builds where the top level feature name contains "package." because the generated scripts in a feature build contain the top level feature id.

Final Notes

The change I have outlined here mas made to fix a specific problem with the Orion build. Care must be taken when applying these techniques to other problems and builders.

The exact details here have been modified from the changes I actually made, so I have not actually tested the scripts as they are written above. Specifically, the condition on the overridden target has been added.

This specific problem can also be fixed in itself, this is tracked by bug 336020.

Thursday, January 27, 2011

Building from Git

Git was introduced at Eclipse about a year ago. Projects are slowly migrating over to use git as the SCM system instead of CVS or SVN. When IBM made its initial contribution for the new Orion project, we migrated from internal CVS servers to git at This post will give an overview of the changes I had to make to our releng setup to start building from git.

There have been a few PDE/Build bug fixes in 3.7 to support building from Git. I recommend using a recent 3.7 build as your base builder. 3.7M5 is due out this week.

General Setup

The Orion releng build is a relatively standard p2 enabled PDE/Build product build.

There are a few things that need to be done to get the build working with git:
  1. Bootstrapping the builder
  2. Getting map files
  3. Fetching source from Git
The e4 builds consume source code from git repositories, but the releng project and map files are still in CVS. Only the 3rd step here was required for e4. The entire Orion project including releng project and mapfiles is in git so we need all three.

Bootstrapping the Build

The Orion releng builds run via cron job on We need a small shell script that can get the Orion releng project from git and start everything off. We do this using the git archive command:
git archive --format=tar --remote=/gitroot/e4/org.eclipse.orion.server.git master
 releng/org.eclipse.orion.releng | tar -xf -
The build machine has local access to the git repository, if we were running from somewhere else, this would change to something like --remote=ssh://

This will get the releng project into the current working directory, at which point we can invoke ant on it.

Getting map files from Git

PDE/Build uses map files to fetch our code from source repositories. The first step to this is getting the map files themselves.

PDE/Build comes with default support to fetch map files from CVS which is controlled by a few properties (see Fetch phase Control). This obviously doesn't apply here. However, this step is fully customizable using the customTargets.xml file.

All we need to do is copy the file into our builder and modify the getMapFiles target. We can then use the git archive command to get our map files. It would look something like this:
<target name="getMapFiles"  unless="skipMaps">
 <mkdir dir="${buildDirectory}/maps" />
 <exec executable="git" dir="${buildDirectory}/maps"
    <arg line="archive -format=tar" />
    <arg line="--remote=/gitroot/e4/org.eclipse.orion.server.git" />
    <arg line="master releng/org.eclipse.orion.releng/maps" />
 <untar src="${buildDirectory}/maps/maps.tar" dest="${buildDirectory}/maps" />
Because the "| tar -xf -" we used earlier is a redirection done by the shell, that doesn't work when we invoke git from ant. Here we specify a file to hold the output of the archive command, this ends up being a tar file which we can just untar.

Fetching source from Git

PDE/Build has an extension point where fetch script generators for different repositories can be plugged in. The EGit project provides an implementation for this extension point.
The org.eclipse.egit.fetchfactory bundle is available from the p2 repository. Install that bundle into the eclipse install that is used to run your build.

Git Map Files

Once we have the egit fetchfactory, all we need to do is update our map files with entries for GIT. Here is an example map file entry from Orion:
  • tag is the tag to use when fetching the bundle from git
  • repo is the path to the git repository. In order to omit the user from the repository path, the build needs to run as a user who has ssh access to the git repo at
  • path is the path within the git repository to the project we are interested in.

Final Details

  • The EGit fetch factory works by cloning the git repository to a local directory, checking out the tag and then copying the project over to the build Directory. Builders can set the fetchCacheLocation property to specify a local directory where the git clones can be kept. This location may be reused from build to build to avoid having to re-download the entire repository each build.
  • "Nightly" builds are set up to build the latest code from HEAD. For CVS, this is usually accomplished by setting "fetchTag=HEAD" to override the map file entries. For Git you would use "fetchTag=origin/master". If you are using both CVS and GIT you can set both with "fetchTag=CVS=HEAD,GIT=origin/master".
  • The Eclipse Platform team uses the releng tool plugin to manage their map files in CVS, there is not yet an equivalent tool for git. See the Orion Releng and E4/Git wiki pages for instruction on how to manage map files.

Thursday, January 20, 2011

Releng tricks from e4 and Orion

In the last couple of months I've found myself in charge of two releng builds: e4 and Orion. The e4 build is actually 2 pieces: building the Eclipse 4.1 SDK and building additional e4 bundles which are not part of the SDK by default.

Being the PDE/Build project lead gives me a unique perspective on this entire process so I thought I would share some tip and tricks for specific problems I encountered.

The first covers how we do signing when building the Eclipse 4.1 SDK.

Signing the Eclipse 4.1 SDK

We produce signed bundles in our builds. The specifics of how to do this have already been worked out by Kim. Essentially we provide a zip file that gets sent off to to be signed.

For the 4.1 SDK there is a slight twist to the problem. The 4.1 SDK is mostly composed of binary bundles we reconsume from 3.7 together with some new e4 bundles that we compile ourselves. We really only want to sign the bundles that we compiled ourselves and avoid resigning the binary bundles.

The trick for creating an archive containing only the bundles we compiled works best for p2 enabled builds (using p2.gathering=true).

Custom Assembly Targets

PDE/Build supports customization of your build using provided template files. In particular we are interested in the customAssembly.xml script. This provides targets that will be invoked by PDE/Build during the packaging and assembly phases of the build.

Specifically, there is a target which is invoked for every bundle that we are building immediately after the contents for that bundle are published into the p2 repository. There is another target which is called after we are finished with all the bundles.

The idea is that we use the target to record which bundles we compiled, and the to sign these bundles and update the p2 repository. At the time is called, the p2 repository will contain binary bundles as well as the compiled ones which is why we need a record of which ones to sign.

The script looks something like this:

<project name="CustomAssemble.overrides" default="noDefault">
<import file="${eclipse.pdebuild.templates}/headless-build/customAssembly.xml" />

<!-- every time is called, we will record the project being built -->
<target name="" >
<echo append="true" file="${buildDirectory}/built.list"
message="**/${projectName}.jar${line.separator}" />

<target name="" >
<property name="signingArchive" value="${buildDirectory}/${buildLabel}/sign-${buildId}.zip" />
<zip zipfile="${signingArchive}" basedir="${}"
includesFile="${buildDirectory}/built.list" />

<!-- sign! -->
<ant antfile="${builder}/sign.xml" dir="${basedir}" target="signMasterFeature" >
<property name="signingArchive" value="${signingArchive}" />

<!--unzip signed archive over top of the repository -->
<unzip dest="${}" src="${signingArchive}" />

<!--update repository with new checksums for signed bundles -->
<p2.process.artifacts repositoryPath="file://${}" />
Some notes:
  • ${projectName} is a property set by PDE/Build and it contains the bundle symbolic name and the version of the bundle being built (ie org.eclipse.foo_1.0.0.v2011).
  • The bundles are recorded in built.list in the form of an ant include pattern.
  • The signing archive is created from the p2 repository using the generated built.list as an includes file.
  • The sign.xml script being used is the one from the e4 build and is available here.
  • The p2 artifact repository contains checksums for each artifact, so after extracting the signed archive over top of the repository, we need to update the repository to recalculate these checksums.
  • I have not actually tested the above ant snippet, it may require some tweaks. The general strategy is based on what we do in the e4 build but some of the details have changed.