Say you publish a p2 repository for your Eclipse bundles and features. Typically your bundles and features will depend on something external (other Eclipse bundles and features). The users of your p2 repository will have to also use the p2 repositories of the dependencies of your software otherwise they won’t be able to install your software. If your software only relies on standard Eclipse bundles and features, that is, something that can be found in the standard Eclipse central update site, you should have no problem: your users will typically have the Eclipse central update site already configured in their Eclipse installations. So, unless your software requires a specific version of an Eclipse dependency, you should be fine.
What happens instead if your software relies on external dependencies that are available only in other p2 sites? Or, put it another way, you rely on an Eclipse project that is not part of the simultaneous release or you need a version different from the one provided by a specific Eclipse release.
You should tell your users to use those specific p2 sites as well. This, however, will decrease the user experience at least from the installation point of view. One would like to use a p2 site and install from it without further configurations.
To overcome this issue, you should make your p2 repository somehow self-contained. I can think of 3 alternative ways to do that:
- If you build with Tycho (which is probably the case if you don’t do releng stuff manually), you could use <includeAllDependencies> of the tycho-p2-repository plugin to “to aggregate all transitive dependencies, making the resulting p2 repository self-contained.” Please keep in mind that your p2 repository itself will become pretty huge (likely a few hundred MB), so this might not be feasible in every situation.
- You can put the required p2 repositories as children of your composite update site. This might require some more work and will force you to introduce composite update sites just for this. I’ve written about p2 composite update sites many times in this blog in the past, so I will not consider this solution further.
- You can use p2 site references that are meant just for the task mentioned so far and that have been introduced in the category.xml specification for some time now. The idea is that you put references to the p2 sites of your software dependencies and the corresponding content metadata of the generated p2 repository will contain links to the p2 sites of dependencies. Then, p2 will automatically contact those sites when installing software (at least from Eclipse, from the command line we’ll have to use specific arguments as we’ll see later). Please keep in mind that this mechanism works only if you use recent versions of Eclipse (if I remember correctly this has been added a couple of years ago).
In this blog post, I’ll describe such a mechanism, in particular, how this can be employed during the Tycho build.
The simple project used in this blog post can be found here: https://github.com/LorenzoBettini/tycho-site-references-example. You should be able to easily reuse most of the POM stuff in your own projects.
IMPORTANT: To benefit from this, you’ll have to use at least Tycho 2.4.0. In fact, Tycho started to support site references only a few versions ago, but only in version 2.4.0 this has been implemented correctly. (I personally fixed this: https://github.com/eclipse/tycho/issues/141.) If you use a (not so) older version, e.g., 2.3.0, there’s a branch in the above GitHub repository, tycho-2.3.0, where some additional hacks have to be performed to make it work (rewrite metadata contents and re-compress the XML files, just to mention a few), but I’d suggest you use Tycho 2.4.0.
There’s also another important aspect to consider: if your software switches to a different version of a dependency that is available on a different p2 repository, you have to update such information consistently. In this blog post, we’ll deal with this issue as well, keeping it as automatic (i.e., less error-prone) as possible.
The example project
The example project is very simple:
- parent project with the parent POM;
- a plugin project created with the Eclipse wizard with a simple handler (so it depends on org.eclipse.ui and org.eclipse.core.runtime);
- a feature project including the plugin project. To make the example more interesting this feature also requires, i.e., NOT includes, the external feature org.eclipse.xtext.xbase. We don’t actually use such an Xtext feature, but it’s useful to recreate an example where we need a specific p2 site containing that feature;
- a site project with category.xml that is used to generate during the Tycho build our p2 repository.
To make the example interesting the dependency on the Xbase feature is as follows
1 2 3 |
<requires> <import feature="org.eclipse.xtext.xbase" version="2.25.0" match="compatible"/> </requires> |
So we require version 2.25.0.
The target platform is defined directly in the parent POM as follows (again, to keep things simple):
1 2 3 4 5 6 7 8 9 10 11 12 |
<repositories> <repository> <id>2020-12</id> <layout>p2</layout> <url>https://download.eclipse.org/releases/2020-12</url> </repository> <repository> <id>2.25.0</id> <layout>p2</layout> <url>https://download.eclipse.org/modeling/tmf/xtext/updates/releases/2.25.0</url> </repository> </repositories> |
Note that I explicitly added the Xtext 2.25.0 site repository because in the 2020-12 Eclipse site Xtext is available with a lower version 2.24.0.
This defines the target platform we built (and in a real example, hopefully, tested) our bundle and feature.
The category.xml initially is defined as follows
1 2 3 4 5 6 7 8 9 10 11 |
<?xml version="1.0" encoding="UTF-8"?> <site> <feature id="org.example.feature" version="0.0.0"> <category name="org.example.category"/> </feature> <category-def name="org.example.category" label="P2 Example Composite Repository"> <description> P2 Example Repository </description> </category-def> </site> |
The problem
If you generate the p2 repository with the Maven/Tycho build, you will not be able to install the example feature unless Xtext 2.25.0 and its dependencies can be found (actually, also the standard Eclipse dependencies have to be found, but as said above, the Eclipse update site is already part of the Eclipse distributions). You then need to tell your users to first add the Xtext 2.25.0 update site. In the following, we’ll handle this.
A manual, and thus cumbersome, way to verify that is to try to install the example feature in an Eclipse installation pointing to the p2 repository generated during the build. Of course, we’ll keep also this verification mechanism automatic and easy. So, before going on, following a Test-Driven approach (which I always love), let’s first reproduce the problem in the Tycho build, by adding this configuration to the site project (plug-in versions are configured in the pluginManagement section of the parent POM):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
<properties> <build.destination>${project.build.directory}/installed-plugins</build.destination> <features>org.example.feature.feature.group</features> <sites>file:/${project.build.directory}/repository</sites> </properties> ... <plugin> <groupId>org.eclipse.tycho.extras</groupId> <artifactId>tycho-eclipserun-plugin</artifactId> <executions> <execution> <id>verify-feature-installation</id> <configuration> <jvmArgs>-Declipse.p2.mirrors=true</jvmArgs> <applicationsArgs> <args>-consoleLog</args> <args>-application</args> <args>org.eclipse.equinox.p2.director</args> <args>-nosplash</args> <args>-followReferences</args> <args>-destination</args> <args>${build.destination}</args> <args>-repository</args> <args>${sites}</args> <args>-installIUs</args> <args>${features}</args> </applicationsArgs> </configuration> <goals> <goal>eclipse-run</goal> </goals> <phase>verify</phase> </execution> </executions> <configuration> <repositories> <repository> <id>2020-12</id> <layout>p2</layout> <url>https://download.eclipse.org/releases/2020-12</url> </repository> </repositories> <dependencies> <dependency> <artifactId>org.eclipse.ant.core</artifactId> <type>eclipse-plugin</type> </dependency> <dependency> <artifactId>org.apache.ant</artifactId> <type>eclipse-plugin</type> </dependency> <dependency> <artifactId>org.eclipse.equinox.p2.repository.tools</artifactId> <type>eclipse-plugin</type> </dependency> <dependency> <artifactId>org.eclipse.equinox.p2.core.feature</artifactId> <type>eclipse-feature</type> </dependency> <dependency> <artifactId>org.eclipse.equinox.p2.extras.feature</artifactId> <type>eclipse-feature</type> </dependency> <dependency> <artifactId>org.eclipse.osgi.compatibility.state</artifactId> <type>eclipse-plugin</type> </dependency> <dependency> <artifactId>org.eclipse.equinox.ds</artifactId> <type>eclipse-plugin</type> </dependency> <dependency> <artifactId>org.eclipse.core.net</artifactId> <type>eclipse-plugin</type> </dependency> </dependencies> </configuration> </plugin> |
The idea is to run the standard Eclipse p2 director application through the tycho-eclipserun-plugin. The dependency configuration is standard for running such an Eclipse application. We try to install our example feature from our p2 repository into a temporary output directory (these values are defined as properties so that you can copy this plugin configuration in your projects and simply adjust the values of the properties). Also, the arguments passed to the p2 director are standard and should be easy to understand. The only non-standard argument is -followReferences that will be crucial later (for this first run it would not be needed).
Running mvn clean verify should now highlight the problem:
1 2 3 4 5 6 7 8 9 10 11 |
!ENTRY org.eclipse.equinox.p2.director ... !MESSAGE Cannot complete the install because one or more required items could not be found. !SUBENTRY 1 org.eclipse.equinox.p2.director... !MESSAGE Software being installed: Feature 2.0.0.v20210827-1002 (org.example.feature.feature.group 2.0.0.v20210827-1002) !SUBENTRY 1 org.eclipse.equinox.p2.director ... !MESSAGE Missing requirement: Feature 2.0.0.v20210827-1002 (org.example.feature.feature.group 2.0.0.v20210827-1002) requires 'org.eclipse.equinox.p2.iu; org.eclipse.xtext.xbase.feature.group [2.25.0,3.0.0)' but it could not be found |
This would mimic the situation your users might experience.
The solution
Let’s fix this: we add to the category.xml the references to the same p2 repositories we used in our target platform. We can do that manually (or by using the Eclipse Category editor, in the tab Repository Properties):
The category.xml initially is defined as follows
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?xml version="1.0" encoding="UTF-8"?> <site> <feature id="org.example.feature" version="0.0.0"> <category name="org.example.category"/> </feature> <category-def name="org.example.category" label="P2 Example Composite Repository"> <description> P2 Example Repository </description> </category-def> <repository-reference location="http://download.eclipse.org/releases/2020-12" enabled="true" /> <repository-reference location="http://download.eclipse.org/modeling/tmf/xtext/updates/releases/2.25.0" enabled="true" /> </site> |
Now when we create the p2 repository during the Tycho build, the content.xml metadata file will contain the references to the p2 repository (with a syntax slightly different, but that’s not important; it will contain a reference to the metadata repository and to the artifact repository, which usually are the same). Now, our users can simply use our p2 repository without worrying about dependencies! Our p2 repository will be self-contained.
Let’s verify that by running mvn clean verify; now everything is fine:
1 2 3 4 5 |
!ENTRY org.eclipse.equinox.p2.director ... !MESSAGE Overall install request is satisfiable !SUBENTRY 1 org.eclipse.equinox.p2.director ... !MESSAGE Add request for Feature 2.0.0.v20210827-1009 (org.example.feature.feature.group 2.0.0.v20210827-1009) is satisfiable |
Note that this requires much more time: now the p2 director has to contact all the p2 sites defined as references and has to also download the requirements during the installation. We’ll see how to optimize this part as well.
In the corresponding output directory, you can find the installed plugins; you can’t do much with such installed bundles, but that’s not important. We just want to verify that our users can install our feature simply by using our p2 repository, that’s all!
You might not want to run this verification on every build, but, for instance, only during the build where you deploy the p2 repository to some remote directory (of course, before the actual deployment step). You can easily do that by appropriately configuring your POM(s).
Some optimizations
As we saw above, each time we run the clean build, the verification step has to access remote sites and has to download all the dependencies. Even though this is a very simple example, the dependencies during the installation are almost 100MB. Every time you run the verification. (It might be the right moment to stress that the p2 director will know nothing about the Maven/Tycho cache.)
We can employ some caching mechanisms by using the standard mechanism of p2: bundle pool! This way, dependencies will have to be downloaded only the very first time, and then the cached versions will be used.
We simply introduce another property for the bundle pool directory (I’m using by default a hidden directory in the home folder) and the corresponding argument for the p2 director application:
1 2 3 4 5 6 |
... <bundlepool>${user.home}/.bundlepool</bundlepool> ... <args>-bundlepool</args> <args>${bundlepool}</args> ... |
Note that now the plug-ins during the verification step will NOT be installed in the specified output directory (which will store only some p2 properties and caches): they will be installed in the bundle pool directory. Again, as said above, you don’t need to interact with such installed plug-ins, you only need to make sure that they can be installed.
In a CI server, you should cache the bundle pool directory as well if you want to benefit from some speed. E.g., this example comes with a GitHub Actions workflow that stores also the bundle pool in the cache, besides the .m2 directory.
This will also allow you to easily experiment with different configurations of the site references in your p2 repository. For example, up to now, we put the same sites used for the target platform. Referring to the whole Eclipse releases p2 site might be too much since it contains all the features and bundles of all the projects participating in Eclipse Simrel. In the target platform, this might be OK since we might want to use some dependencies only for testing. For our p2 repository, we could tweak references so that they refer only to the minimal sites containing all our features’ requirements.
For this example we can replace the 2 sites with 4 small sites with all the requirements (actually the Xtext 2.25.0 is just the same as before):
1 2 3 4 |
<repository-reference location="http://download.eclipse.org/eclipse/updates/4.18" enabled="true" /> <repository-reference location="http://download.eclipse.org/modeling/tmf/xtext/updates/releases/2.25.0" enabled="true" /> <repository-reference location="http://download.eclipse.org/tools/orbit/downloads/2020-12" enabled="true" /> <repository-reference location="http://download.eclipse.org/modeling/emf/emf/builds/release/latest" enabled="true" /> |
You can verify that removing any of them will lead to installation failures.
The first time this tweaking might require some time, but you now have an easy way to test this!
Keeping things consistent
When you update your target platform, i.e., your dependencies versions, you must make sure to update the site references in the category.xml accordingly. It would be instead nice to modify this information in a single place so that everything else is kept consistent!
We can use again properties in the parent POM:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<properties> ... <eclipse-version>2020-12</eclipse-version> <eclipse-version-number>4.18</eclipse-version-number> <xtext-version>2.25.0</xtext-version> </properties> ... <repositories> <repository> <id>${eclipse-version}</id> <layout>p2</layout> <url>https://download.eclipse.org/releases/${eclipse-version}</url> </repository> <repository> <id>${xtext-version}</id> <layout>p2</layout> <url>https://download.eclipse.org/modeling/tmf/xtext/updates/releases/${xtext-version}</url> </repository> </repositories> |
We want to rely on such properties also in the category.xml, relying on the Maven standard mechanism of copy resources with filtering.
We create another category.xml in the subdirectory templates of the site project using the above properties in the site references (at least in the ones where we want to have control on a specific version):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?xml version="1.0" encoding="UTF-8"?> <site> <feature id="org.example.feature" version="0.0.0"> <category name="org.example.category"/> </feature> <category-def name="org.example.category" label="P2 Example Composite Repository"> <description> P2 Example Repository </description> </category-def> <repository-reference location="http://download.eclipse.org/eclipse/updates/${eclipse-version-number}" enabled="true" /> <repository-reference location="http://download.eclipse.org/modeling/tmf/xtext/updates/releases/${xtext-version}" enabled="true" /> <repository-reference location="http://download.eclipse.org/tools/orbit/downloads/${eclipse-version}" enabled="true" /> <repository-reference location="http://download.eclipse.org/modeling/emf/emf/builds/release/latest" enabled="true" /> </site> |
and in the site project we configure the Maven resources plugin appropriately:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<plugin> <artifactId>maven-resources-plugin</artifactId> <executions> <execution> <id>replace-references-in-category</id> <phase>generate-resources</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <outputDirectory>${basedir}</outputDirectory> <resources> <resource> <directory>${basedir}/templates/</directory> <includes> <include>category.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </configuration> </execution> </executions> </plugin> |
Of course, we execute that in a phase that comes BEFORE the phase when the p2 repository is generated. This will overwrite the standard category.xml file (in the root of the site project) by replacing properties with the corresponding values!
By the way, you could use the property eclipse-version also in the configuration of the Tycho Eclipserun plugin seen above, instead of hardcoding 2020-12.
Happy releasing! 🙂
Could you please provide the URL of the specification for the category.xml format? I am failing to find it anywhere.
Unfortunately, I never found that myself either… you might want to ask on some Eclipse forum.
Otherwise, you might want to look at the Git repository of p2.
Thanks for the insight, it was very useful to me. I am just wondering one thing : it seems that the site.xml does not have the same kind of additional repositories tag, so we cannot use it for such matters.
However, category.xml files do not have a “Build all” button equivalent, so my point is we cannot build from Eclipse UI anymore & have to use maven (whether via command line or with a launch configuration). Am I right, or would there be a possible workaround ?
I’m glad you enjoyed the post.
Concerning your question, the “old” update site (i.e., “site.xml” has been deprecated for some time), and you should definitely use “category.xml”.
Using Maven/Tycho is the preferred way from a continuous integration point of view.
However, if you want to generate a p2 repository from Eclipse quickly, you cannot use the “Category” editor, as you have already discovered.
You have to use “File” -> “Export” -> “Deployable Features” and (after selecting the desired features) from the tab “Options” specify to generate a p2 repository and “Categorize repository” (where you have to select a category.xml file).