Showing posts with label Buckminster. Show all posts
Showing posts with label Buckminster. Show all posts

Tuesday, September 20, 2011

Buckminster qualifier replacement

Information:
Buckminster does not seem to be very active anymore. To build Eclipse products, update sites and similar have a look at my tycho tutorials. My Buckminster tutorials are no longer maintained and remein here as reference only.


When your Plug-ins and features use a .qualifier in the version string, Buckminster can replace that with dedicated data. The data to be used can be modified in the .properties file passed to buckminster actions.

To use version information:
qualifier.replacement.*=generator:lastRevision
generator.lastRevision.format=r{0,number,00000}
... will use last revision. Eg creates: com.example.myplugin_1.0.0.r1234 for revision 1234.

To use build date:
qualifier.replacement.*=generator:buildTimestamp
generator.buildTimestamp.format='I'yyyyMMdd'-'HHmm
... uses a timestamp with some constant text ('I', '-'). Eg creates com.example.myplugin_1.0.0.I20110920-1408

Buckminster RCP build with independent features

Information:
Buckminster does not seem to be very active anymore. To build Eclipse products, update sites and similar have a look at my tycho tutorials. My Buckminster tutorials are no longer maintained and remein here as reference only.



When building an RCP application all features are typically part of the product. For example the product we built in my previous post contains the Eclipse Platform and a custom feature.


Now when it comes to updates P2 only regards its root nodes. So when you want to update your custom feature only you will soon run into problems. Andrew Niefer wrote a nice blog entry on this topic.

What we want to do is to install a core product only and afterwards install independent features that are not referenced from the base product. This way all these features can be updated independently.

Concept

We first will create a P2 update site containing everything our final installation might need. The product, RCP stuff like platform launchers and optional features. Afterwards we will use this update site to assemble independent features to our final RCP application.

Our final setup will include 6 Plug-ins/Features, 2 Plug-ins containing code, 2 Features each containing one of the Plug-ins and 2 assembly projects. You can see their dependencies on the diagram below:


 We will start by using the code from our previous examples for publishing an update site and for an RCP build as a basis.

Step 1: Updating the update site

The update site shall include code needed for the RCP Feature, so we need to add it to the Included Features. Open com.example.update.p2/features.xml and add com.example.rcp.feature to the list of Included Features.

This will trigger a build for the RCP core feature when the update site is built.

Step 2: Adapting target platform

If you only build for your development platform you can skip this step.

We need to add the Delta Pack to our target platform we defined for the RCP build. Download the delta pack from Eclipse. Go to http://download.eclipse.org/eclipse/downloads/, switch to the 3.x downloads. Afterwards select your release from Latest Releases, find and download the Delta Pack. This package contains platform specific executables so we can build eg a linux RCP on windows.
 Go to Window -> Preferences -> Plug-in Development -> Target Platform. Edit your custom Target Definition and add the location of the eclipse folder located inside the downloaded zip.

You can find a more detailed example on the Target Platform by Ralf Ebert.

Step 3: RCP Build Project

Our com.example.rcp.releng project needs some updates. First we update the ant file build/createProduct.ant.
<project>
     <pathconvert property="equinox.launcher.jar">
       <first count="1">
         <sort>
           <fileset dir="${eclipse.home}/plugins" includes="**/org.eclipse.equinox.launcher_*.jar"/>
           <reverse xmlns="antlib:org.apache.tools.ant.types.resources.comparators">
             <date/>
           </reverse>
         </sort>
       </first>
     </pathconvert>
    
    <target name="create.product">
        <property name="installableIUs" value="${iu},${features}" />        
        <property name="destination" location="${sp:destination}"/>
        <delete dir="${destination}"/>
        <mkdir dir="${destination}"/>
        <makeurl property="repository" file="${sp:updateSite}"/>
        <echoproperties/>
        <echo message="============================== RCP Settings =============================="/>
        <echo message="Repository:  ${repository}"/>
        <echo message="Destination: ${destination}"/>
        <echo message="Product:     ${iu}"/>
        <echo message="Features:    ${features}"/>
        <echo message="IUs:         ${installableIUs}"/>
        <echo message="target:      ${target.os}.${target.ws}.${target.arch}"/>
        <echo message="============================== Building RCP =============================="/>
        <java jar="${equinox.launcher.jar}" fork="true" failonerror="true" >
            <arg value="-application"/>
            <arg value="org.eclipse.equinox.p2.director"/>
            <arg value="-repository"/>
            <arg value="${repository}"/>
            <arg value="-destination"/>
            <arg value="${destination}"/>
            <arg value="-profile"/>
            <arg value="${product.profile}"/>
            <arg value="-profileProperties" />
            <arg value="org.eclipse.update.install.features=true" />
            <arg value="-installIU"/>
            <arg value="${installableIUs}"/>
            <arg value="-p2.os" />
            <arg value="${target.os}" />
            <arg value="-p2.ws" />
            <arg value="${target.ws}" />
            <arg value="-p2.arch" />
            <arg value="${target.arch}" />
            <arg value="-consoleLog"/>
            <arg value="-roaming"/>
        </java>
    </target>
</project>
The director used for the build process now gets a list of installable units (IUs). This list includes our product followed by all independent features we wish to install.

Now change buckminster.cspex to
<?xml version="1.0" encoding="UTF-8"?>
<cspecExtension xmlns:com="http://www.eclipse.org/buckminster/Common-1.0"
    xmlns="http://www.eclipse.org/buckminster/CSpec-1.0">

    <dependencies>
        <dependency name="com.example.update.p2" componentType="eclipse.feature" />
    </dependencies>
    <actions>
        <public name="create.product" actor="ant">
            <actorProperties>
                <property key="buildFile" value="build/createProduct.ant" />
                <property key="targets" value="create.product" />
            </actorProperties>
            <properties>
                <property key="profile" value="${product.profile}" />
                <property key="iu" value="${product.id}" />
                <property key="features" value="${product.features}" />
            </properties>
            <prerequisites alias="updateSite">
                <attribute name="site.p2.publish" component="com.example.update.p2" />
            </prerequisites>
            <products alias="destination" base="${product.destination}">
                <path path="${product.name}.${target.ws}.${target.os}.${target.arch}/" />
            </products>
        </public>

        <public name="create.product.zip" actor="ant">
            <actorProperties>
                <property key="buildFileId" value="buckminster.pdetasks" />
                <property key="targets" value="create.zip" />
            </actorProperties>
            <prerequisites alias="action.requirements">
                <attribute name="create.product" />
            </prerequisites>
            <products alias="action.output" base="${product.destination}">
                <path path="${product.name}.${target.ws}.${target.os}.${target.arch}.zip" />
            </products>
        </public>
    </actions>
</cspecExtension>
This will give us 2 new actions: 
  • create.product to build our RCP applicaton
  • create.product.zip which will build and zip our application.

Finally change buckminster_product.properties to
# Where all the output should go
buckminster.output.root=C:/Build/BuildArtifacts

# Where the temp files should go
buckminster.temp.root=${user.home}/tmp

# How .qualifier in versions should be replaced
qualifier.replacement.*=generator:lastRevision

# update site settings
updatesite.destination=C:/Build/UpdateSite

# rcp settings
product.id=com.example.rcp.core.myproduct
product.name=MyProduct

# leave empty when no features are needed, but do NOT comment this out
# multiple features can be defined as a comma separated list
product.features=com.example.myfeature.feature.group
product.profile=ExampleProfile

product.destination=C:/Build/Sample RCP

target.os=win32
target.ws=win32
target.arch=x86
This property file is used for building the update site and the product. Therefore we need some update site properties too.
  • product.name
    will be used for the product build folder and the zip file. It will not be used anywhere else.
  • product.features
    a comma separated list of all features that should be installed independently from the base product. So if your base product already includes FeatureA, do not add it here! The property may be left empty. In this case you will get the same results as from Buckminster RPC build.
     
  • product.profile
    name of the P2 profile to be created. This will be part of your final RPC application
  • target.X
    Needs to be set to a defined target platform. We cannot use * here anymore as we need to build an RCP dedicated to a specific platform.

    Building for a dedicated platform also influences the content of our update site. All Plug-ins/Features built using this properties file will be built for the defined target only. This might be of interest when you use code that is platform dependent. You still can build your update site only to keep it platform independent.

Step 4: Build the product

When you are done right click on your com.example.rcp.releng project and select Buckminster -> Invoke Action... 

Select create.product and enter the path to your buckminster_product.properties file. After hitting OK you can find your RCP product in C:\Build\Sample RCP.

Our product consists of the consists of the core product My RCP Example which includes the Platform and a custom made Feature My Base Features. Additionally it contains another feature Myfeature that is independent of the core product. Hence we can update Myfeature without updating My RCP Example.


Tuesday, September 13, 2011

Buckminster RCP Build

Information:
Buckminster does not seem to be very active anymore. To build Eclipse products, update sites and similar have a look at my tycho tutorials. My Buckminster tutorials are no longer maintained and remein here as reference only.


In my previous post we were pubishing an update site via Buckminster. Now we are going to build our RCP product.There is a nice tutorial from Ralf Ebert on this topic and this post makes heavy use of it.

Source code for this tutorial is available on googlecode as a single zip archive, as a Team Project Set or you can checkout the SVN projects directly.

Step 1: Create target definition

Open the global preferences and navigate to Plug-in Development -> Target Platform. Create a new target with Add... and select Nothing: Start with an empty target definition. Add... components from a Software Site. Add the Indigo update site (http://download.eclipse.org/releases/indigo). Deselect Group by Category and Include required software. Check Include all environments and reactivate Include required software. Afterwards add
  • Eclipse Platform Launchers
  • Eclipse Platform SDK (not necessarily needed for you RCP)
  • Eclipse RCP SDK

Click Finish. You should end up with a target definition like this:



Name your target accordingly. After creation make sure your target is activated.

Step 2: Create product

Create a new Plug-in project called com.example.rcp.core. We do not want an Activator, contributions to the UI or create an RCP. So deactivate all of them.
Within the Plug-in create a new Product Configuration named example.product. Now we need to define our product in detail. Set the Name to "My RCP Example", then click on New... to create a Product Definition.
Set the Product ID to myproduct and the Application to org.eclipse.ui.ide.workbench.


By creating the Product Definition a plugin.xml file is created. Add it to your build.properties file.

Create a new Feature Project called com.example.rcp.feature and set the Feature Name to My Base Features. Add org.eclipse.platform to the included features. Add com.example.rcp.core to the Plug-ins.

Now back to our product: select the product configuration is based on features. On the Dependencies tab add
  • org.eclipse.platform
  • com.example.rcp.feature
Give it a try by launching your product from the overview tab.

Step 3: Create build environment

We are creating a separate project for release engineering and for opur build files. So create a new Feature Project called com.example.rcp.releng. Add com.example.rcp.feature to the Included Features.

Create a new file buckminster_product.properties and add following content:
# Where all the output should go
buckminster.output.root=${user.home}/Build/Example RCP

# Where the temp files should go
buckminster.temp.root=${user.home}/tmp

# How .qualifier in versions should be replaced
qualifier.replacement.*=generator:lastRevision

product.id=com.example.rcp.core.myproduct
product.profile=ExampleProfile

# Windows 32
target.os=win32
target.ws=win32
target.arch=x86

# Linux
#target.os=linux
#target.ws=gtk
#target.arch=x86
The product.id needs to match with the Product identifier from the Product Definition (highlighted red in the screenshot above).

Now we need a new action for buckminster. We create one by adding a file buckminster.cspex with following content:
<?xml version="1.0" encoding="UTF-8"?>
<cspecExtension xmlns:com="http://www.eclipse.org/buckminster/Common-1.0"
                    xmlns="http://www.eclipse.org/buckminster/CSpec-1.0">
    <actions>
        <public name="create.product" actor="ant">
            <actorProperties>
                <property key="buildFile" value="build/createProduct.ant" />
                <property key="targets" value="create.product" />
            </actorProperties>
            <properties>
                <property key="profile" value="${product.profile}" />
                <property key="iu" value="${product.id}" />
            </properties>
            <prerequisites alias="repository">
                <attribute name="site.p2" />
            </prerequisites>
            <products alias="destination" base="${buckminster.output}">
                <path path="${target.ws}.${target.os}.${target.arch}/" />
            </products>
        </public>
    </actions>
</cspecExtension>
This action refers to an ant file build/createProduct.ant which we need to create now.
<project>
     <pathconvert property="equinox.launcher.jar">
       <first count="1">
         <sort>
           <fileset dir="${eclipse.home}/plugins" includes="**/org.eclipse.equinox.launcher_*.jar"/>
           <reverse xmlns="antlib:org.apache.tools.ant.types.resources.comparators">
             <date/>
           </reverse>
         </sort>
       </first>
     </pathconvert>

    <target name="create.product">
        <property name="destination" location="${sp:destination}"/>
        <delete dir="${destination}"/>
        <makeurl property="repository" file="${sp:repository}"/>
        <mkdir dir="${destination}"/>
        <echoproperties/>
        <echo message="Repository:  ${repository}"/>
        <echo message="Destination: ${destination}"/>
        <echo message="IU:          ${iu}"/>
        <java jar="${equinox.launcher.jar}" fork="true" failonerror="true" >
            <arg value="-application"/>
            <arg value="org.eclipse.equinox.p2.director"/>
            <arg value="-repository"/>
            <arg value="${repository}"/>
            <arg value="-destination"/>
            <arg value="${destination}"/>
            <arg value="-profile"/>
            <arg value="${profile}"/>
            <arg value="-profileProperties" />
            <arg value="org.eclipse.update.install.features=true" />
            <arg value="-installIU"/>
            <arg value="${iu}"/>
            <arg value="-p2.os" />
            <arg value="${target.os}" />
            <arg value="-p2.ws" />
            <arg value="${target.ws}" />
            <arg value="-p2.arch" />
            <arg value="${target.arch}" />
            <arg value="-consoleLog"/>
            <arg value="-roaming"/>
        </java>
    </target>
</project>
Step 4: Executing the build job

Right click on your com.example.rcp.releng project and select Buckminster -> Invoke Action... Select create.product and add the location of your buckminster_product.properties file. Hit OK and wait for the build to finish.

You can find your final product under

${user.home}/Build/Example RCP/com.example.rcp.releng_1.0.0-eclipse.feature/win32.win32.x86
The Installation Details list our product with its sub components as we declared them in our Product Configuration.


Details: How things work together

When the build process is started, buckminster first creates a P2 update site containing all the features and plugins referenced from the com.example.rcp.releng feature. This will create a repository for our RCP installation afterwards. If our RCP needs additional stuff, we need to take care that we add these dependencies to our releng feature.

Once the update site is done, buckminster calls the director app - a special eclipse application used for headless installs of installable units (IUs, like products or features) and installs the given product to a dedicated location. Therefore it uses the pre-built update site as install repository.

The buckminster create.product.action collects some parameters from the properties file, builds the P2 update site and then triggers the ant file which actually handles the install process.

Adapt to your own needs

Typically you would create your own product with its own dependencies. Once you have a runnable product, create a releng Feature Project like in step 3. Change the product.id in the buckminster_product.properties file and add your product dependencies to the Included Features of your releng Feature Project.


Monday, September 12, 2011

Publishing a Buckminster Update Site

Information:
Buckminster does not seem to be very active anymore. To build Eclipse products, update sites and similar have a look at my tycho tutorials. My Buckminster tutorials are no longer maintained and remein here as reference only.


Building a p2 update site with Buckminster is fairly easy. Following the tutorial "Building a p2 Update Site" from BuckyBook will give you a working update site in minutes. What I was missing was some way to publish the resulting p2 site automatically. Fortunately Buckminster actions can easily be extended. What we want to do is create a new ant task that copies over the p2 site to a user defined location.

Source code for this tutorial is available on googlecode as a single zip archive, as a Team Project Set or you can checkout the SVN projects directly.

Step 1: Prerequisites

Before we can extend our build process we need to create an update site according to the Buckminster tutorial.

Plug-in Project

Create a new Plug-in Project named com.example.myplugin. On the 2nd page enable This plug-in will make contributions to the UI. On the 3rd page select the template Plug-in with a view. Leave everything else unchanged.

Feature Project

Create a new Feature Project named com.example.myfeature. Add the plug-in com.example.myplugin to the feature.

Update Site Feature

Buckminster needs a unique Feature Project for building its update site. So create a new Feature Project named com.example.update.p2. Add com.example.myfeature to the included features.

Create a new file called buckminster_p2.properties with following content:
# Where all the output should go
buckminster.output.root=${user.home}/Build/Example P2

# Where the temp files should go
buckminster.temp.root=${user.home}/tmp

# How .qualifier in versions should be replaced
qualifier.replacement.*=generator:lastRevision

target.os=*
target.ws=*
target.arch=*

Creating categories

A nice update site groups its features in categories. So lets create a new Category Definition in com.example.update.p2. The editor is rather straight forward. Create categories with a unique ID and a display Name. Afterwards add your com.example.myfeature feature to the category.


Whenever your feature version number changes, you need to re-add your feature again.
Now right click on your com.example.update.p2 Feature Project and select Buckminster -> Invoke Action...



Select the site.p2 action and enter your buckminster_p2.properties properties file location. After hitting OK your update site will be built. Following this example it will be located at

${user.home}/Example P2/com.example.update.p2_1.0.0-eclipse.feature/site.p2

Step 2: Generating a publish action

To create a publish action we need to create an ant file doing the work and a specification telling buckminster what to do. Lets start by declaring the destination for the publish action.

Open buckminster_p2.properties and add following line

updatesite.destination=${user.home}/Build/UpdateSite


This will be our target destination. Now create a new file buckminster.cspex in your com.example.update.p2 Feature Project with following content:

<?xml version="1.0" encoding="UTF-8"?>
<cspecExtension xmlns:com="http://www.eclipse.org/buckminster/Common-1.0"
                    xmlns="http://www.eclipse.org/buckminster/CSpec-1.0">
    <actions>
        <public name="site.p2.publish" actor="ant">
            <actorProperties>
                <property key="buildFile" value="build/publishUpdateSite.ant" />
                <property key="targets" value="publish.p2" />
            </actorProperties>
            <properties>
                <property key="source" value="${buckminster.output}/site.p2/" />
                <property key="destination" value="${updatesite.destination}" />
            </properties>
            <prerequisites alias="repository">
                <attribute name="site.p2" />
            </prerequisites>
            <products base="${updatesite.destination}" upToDatePolicy="ACTOR"/>
        </public>
    </actions>
</cspecExtension>
Line 5 creates an action called site.p2.publish and delares it an ant task.
Line 7 defines the and build file, line 8 the ant target
Line 11,12 define source and destination properties for the ant task
Line 15 defines the buckminster site.p2 action as a prerequisite. This means that first site.p2 is executed, afterwards our ant task is called.

Finally we need to add our ant task. Create a file build/publishUpdateSite.ant and add following lines:

<project>
    <target name="publish.p2">
        <echo message="Source:      ${source}"/>
        <echo message="Destination: ${destination}"/>
        
        <mkdir dir="${destination}"/>
        <copy todir="${destination}" preservelastmodified="true">
            <fileset dir="${source}"/>
        </copy>
    </target>
</project>
This will create the destination folder if not present and copy the p2 site content to that location.

Step 3: Executing the publish action

Save all files, right click on your com.example.update.p2 Feature Project and select Buckminster -> Invoke Action...
Use site.p2.publish action. Take care that the property file location is still correct. Push OK to publish your update site. You can find your site at C:\Build\UpdateSite

The ant task is rather simple and can easily be extended to eg. use WebDav to upload the content to a webserver or to store the update site to SVN, ...

Alternative: Alter site.p2 action directly

If publishing is just a question of copying files we can do this without our custom action and ant task. Instead we can directly modify the target path of the site.p2 action. Therefore set your buckminster.cspex content to

<?xml version="1.0" encoding="UTF-8"?>
<cspecExtension xmlns:com="http://www.eclipse.org/buckminster/Common-1.0"
                    xmlns="http://www.eclipse.org/buckminster/CSpec-1.0">

    <alterActions>
        <public name="site.p2">
            <products base="${updatesite.destination}/" upToDatePolicy="ACTOR"/>
        </public>
    </alterActions> 
</cspecExtension>