Wednesday, July 08, 2009

ADT Part 2: More like the EPP

Yesterday, I posted an example of how to compose a product made up out of updatable sub-components. The first question there was about how this relates to the packages built by the EPP. The EPP packages are just products, much like my ADT. They use their own feature and plugin to brand the Eclipse package and do things like open a different default perspective.

The EPP packages are built slightly different from my first example. They use feature requirements to get version ranges for the sub-components, but they don't currently perform the extra director calls to make those sub-components into updatable roots.

Here is another example that is structured a bit more like the EPP packages.
This example is also available in cvs with the first (dev.eclipse.org:/cvsroot/eclipse/pde-build-home/examples/adt.feature.builder).

[Edit 2013/07/03: Eclipse CVS has migrated to git starting in 2010.  The examples are now available under the examples folder in http://git.eclipse.org/gitroot/pde/eclipse.pde.build.git. The example has not been updated to work with git and may require modifications.]

Features instead of p2.inf


In our first example, we used a p2.inf file to add requirements with version ranges to our product. I did it this way because I didn't have anything to add to the product, and didn't want to bother creating a feature.

Perhaps a more familiar way of doing things would have been to use a feature. So we create a feature "adt.feature", and instead of including our sub-components, we require them using a "compatible" match rule:



I also made my feature include the "org.eclipse.platfom" feature. The adt.product file then just includes adt.feature. We no longer need the p2.inf to add requirements to our product.

Adding Branding


Since we created our own feature for this product, we may as well take the next step and add our own branding plug-in. The first thing I did way create my own product extension in the plugin.xml:
    <extension id="product" point="org.eclipse.core.runtime.products">
      <product application="org.eclipse.ui.ide.workbench" name="ADT Product">
         <property name="aboutText" value="Andrew's Development Tools"/>
         <property name="windowImages" value="icons/icon.gif"/>
         <property name="aboutImage" value="product.gif"/>
      </product>
   </extension>


Note that the word 'product' here is slightly overloaded. There is a 'org.eclipse.core.runtime.products' extension point that defines branding for eclipse, and there is the product itself which is what we are building using the .product file.

This product is just running the normal workbench application, but it uses my own icons and images. We then change our adt.product file to use adt.plugin.product instead of the org.eclipse.platform.ide product extension we were using before.

In my adt.feature I set the branding plug-in to be my new adt.plugin. I also provide the normal about.* files so that my feature shows up in the about dialog.

As a final touch, I made my own splash screen.

Building the new Product


The build script is essentially unchanged from the first example. So I won't bother explaining the details again. The only difference is that I made some minor changes to the builder's build.properties file so that pde.build can find our feature and plugin.



Note that PDE/Build does not follow feature requirements when performing a build and publishing metadata. This means that for the director install to work, you need to have pre-existing metadata for the things that you are requiring. This same requirement exists with the first example where we used a p2.inf file.

Tuesday, July 07, 2009

Composing and updating custom Eclipse distros

I've recently seen a couple of different posts to the newsgroups dealing with problems updating RCP applications using p2. [edit 2009/10/21, update links to forums]

As an example, I've created my own Eclipse product. It is composed of the Eclipse Platform, CVS support, the CDT and Mylyn. I'm calling it the ADT (Andrew's Development Tools).

It's not hard to create a feature based product that includes these things, and do a product build to end up with something like this:



As explained in this newsgroup post, there are two kinds of things that are included in an Eclipse install:
  1. Things that are explicitly installed
  2. Things that are required by the things that are installed.
Here in my example, only my development tools "org.example.adt" is installed, the rest (CDT, CVS, Mylyn) are required by my product.

Only things that are explicitly installed will be searched for when you look for updates. Also, the installed things generally specify the versions of things they require, which makes it hard to install/update those required items independently of the root product. In both the newsgroup postings I referred to above, the problem was trying to install/update one of the required items without updating the root product.

So the question becomes how to allow updating sub-components of the product without updating the product itself.

Composing for Updatability

What we want to do is to update sub-components of the product without updating the root product itself. In this example we do not to allow updating the Eclipse Platform independently, to do that, the user will need to update the product itself.

I have created a example builder to do this. Get it from cvs (dev.eclipse.org:/cvsroot/eclipse/pde-build-home/examples/adt.builder).

[Edit 2013/07/03: Eclipse CVS has migrated to git starting in 2010.  The examples are now available under the examples folder in http://git.eclipse.org/gitroot/pde/eclipse.pde.build.git. The example has not been updated to work with git and may require modifications to run properly.]

We need to do two things:
  1. Use version ranges to include sub-components in our product so that we allow upgrading those components.
  2. Explicitly install those sub-components so they will be found when checking for updates. This is essentially a book-keeping step.

The ADT .product File

There is a adt.builder/product/adt.product file which we will use to run a product build. If we were to include the features for our sub-components in the .product file, then we would end up with requirements on specific versions of those components. Instead we only include the platform feature [1].

To get requirements to our sub-components, we use a p2.inf file to customize the metadata. We add requirements with entries that look like this:
requires.1.namespace = org.eclipse.equinox.p2.iu
requires.1.name = org.eclipse.cvs.feature.group
requires.1.range = [1.1.100, 1.2.0)

requires.2.namespace = org.eclipse.equinox.p2.iu
requires.2.name = org.eclipse.mylyn_feature.feature.group
requires.2.range = [3.2.0, 3.3.0)

...

The .feature.group suffix is the name of the p2 Installable Unit corresponding to the features we are interested in. We specify the version ranges in which we will allow those components to be updated.

The ADT Builder

The adt.builder project includes a buildADT.xml ant script which will run a headless product build for us. The first thing it does is download zips containing the things we need. This example illustrates three different ways of reconsuming metadata.
  1. The CDT and CVS both come as zipped p2 repositories. Things that are not referenced directly by the .product file only need to be available as repositories. We can reuse these zips directly by specifying them as context repositories using jar: urls. See the p2.context.repos property in the adt.builder/build.properties file.
  2. Mylyn is not a p2 repository, it is a zipped old style update site. For this, we use a publisher task to generate p2 metadata for it. [2]
  3. The Eclipse Platform is a p2 repository just like the CDT and CVS. It is similar to the delta pack in that it contains the org.eclipse.equinox.executable feature that is need to get launchers in product builds. Because the platform feature is included directly in the product, we can't just specify the platform as a context repository, we need the bundles available to pde.build like in a normal headless build. To do this we transform the repository using the p2.repo2runnable task. See the transformedRepoLocation and repoBaseLocation properties in the build.properties file. The transformed repository automatically gets included along with the pluginPath property used by pde.build.

Adding additional director calls

In order for our sub-components to be independently updatable, they need to be explicitly installed in our resulting product. By default PDE/Build performs a director install for just the product being built. We can use a customAssembly.xml script to perform additional director[3] calls before the final archive is created.

It looks like this:
<target name="pre.archive">
<ant antfile="${genericTargets}" target="runDirector" inheritAll="true">
<property name="p2.repo" value="${p2.build.repo}"/>
<property name="p2.director.iu" value="org.eclipse.cvs.feature.group"/>
<property name="p2.director.installPath" value="${eclipse.base}"/>
</ant>
...
</target>
We make director calls for each of the sub components we allow to be updated. In the example we do CVS, Mylyn, CDT, and the CDT-Mylyn bridge.

The final result

Run the adt.builder by right-clicking on buildADT.xml and choosing Run As -> Ant Build... Be sure to run in the same JRE as the workspace. After running the build, the results are available under adt.builder/buildDirectory/I.<timstamp>.

Running the resulting product, we see that the CDT, Mylyn and CVS are all showing up as installed roots, and are therefore independently updatable.



Notes

  1. PDE/Build will automatically generate start level configuration information, but only for things that are included in the .product file. If we didn't include the platform feature, or at least the bundles that need start level information, then this would not happen automatically and we would need to handle start levels ourselves. See the help page here for more information of configuring start levels.
  2. We publish the p2 metadata for mylyn into ${p2.build.repo}. This property specifies the location of the p2 repository that will be used internally by the build. Publishing the mylyn metadata here instead of some location specified as a context repository saves the build from mirroring the required IUs into the build repository.
  3. PDE/Build provides a "runDirector" target that can be used to invoke the director. This works by executing the director application in a new process. Normally, this requires setting the "equinoxLauncherJar" property specifying the location of the equinox launcher to use, but because we are calling the director from customAssembly.xml, we inherit this property from the generated assembly scripts.
  4. Running this build produces a properly p2 enabled product. It does not produce a corresponding repository for that product other than the build time repository ${p2.build.repo}. To produce a final repository containing the final product, define the properties p2.metadata.repo and p2.artifact.repo in the build.properties. The product and its requirements will then be automatically mirrored into that repo.