Scala projects with Eclipse PDE Build

PDE Build is the standard build tool from Eclipse. It is used to export OSGi bundles, plugins, features or products. Running in headless mode, PDE Build can be used for automatic builds without a GUI. The Scala IDE does not support PDE Build yet. In order to compile Scala projects you have to do some manual work.

This article relates to an old version of the Scala IDE for Eclipse. Information about the latest version can be found in a newer post!

First of all, you need an Ant script that can be hooked into the build process. The script’s name should be customBuildCallbacks.xml and it should be located at the project’s root:

de.michel-kraemer.myplugin/
  |- bin
  |- META-INF
  |- src
  |- build.properties
  |- customBuildCallbacks.xml

The same directory contains the plugin’s build.properties file. (Please do not mix this one up with the more general build.properties file used for headless builds!) The following lines have to be added to this file to hook the new Ant script into the build process:

customBuildCallbacks=customBuildCallbacks.xml
customBuildCallbacks.inheritall=true

The second line enables access to global properties like ${build.result.folder}.

The Ant script can contain targets with predefined names. They will be called by PDE Build eventually during the build process. You can copy a template, that already contains all predefined targets (but empty) from the following directory: ${eclipse.home}/plugins/org.eclipse.pde.build_*/templates/plugins/

The post.compile.@dot target will be called when the source files are about to be compiled, so that’s the best point to execute the Scala compiler manually. Therefore, some additional Ant tasks have to be defined. These can be found in the Scala Library which is delivered with the Scala IDE. You can automatically search for the required jar files using the following snippet:

<!-- find eclipse.home -->
<pathconvert property="eclipse.home">
    <path location="${eclipse.launcher}" />
    <mapper>
        <!-- map "${eclipse.home}/eclipse.exe" to "${eclipse.home}" -->
        <globmapper from="*/eclipse.exe" to="*" handledirsep="true" />
    </mapper>
</pathconvert>

<!-- find scala bundle -->
<pathconvert property="scala_bundle">
    <path>
        <fileset dir="${eclipse.home}/plugins">
            <include name="scala.library_*" />
        </fileset>
    </path>
</pathconvert>

<!-- find scala tools -->
<pathconvert property="scala_tools_jar">
    <path>
        <fileset dir="${eclipse.home}/plugins">
            <include name="scala.tools.nsc_*" />
        </fileset>
    </path>
</pathconvert>

This places the path to the Scala OSGi bundle in the variable ${scala_bundle}. The variable ${scala_tools_jar} will point to the jar file that contains the Scala Tools and, thus, the Ant tasks.

The library bundle has to be extracted first, so the actual Scala library scala-library.jar can be used in the classpath during the build. Therefore, you can use the temporary build directory ${build.result.folder}:

<unjar dest="${build.result.folder}/scala-library" src="${scala_bundle}" />
<property name="scala_library_jar"
    location="${build.result.folder}/scala-library/lib/scala-library.jar" />

After this, the Ant targets can be defined:

<!-- define scalac task -->
<taskdef resource="scala/tools/ant/antlib.xml">
    <classpath>
        <pathelement location="${scala_tools_jar}" />
        <pathelement location="${scala_library_jar}" />
    </classpath>
</taskdef>

Before the source files can be compiled, a valid classpath has to be created. It consists of the classpath defined by PDE Build @dot.classpath and the Scala library:

<pathconvert property="my.classpath">
    <restrict>
        <path>
            <path refid="@dot.classpath" />
            <pathelement location="${scala_library_jar}" />
        </path>
        <!-- remove libraries from classpath that don't exist (optional) -->
        <rsel:exists />
    </restrict>
</pathconvert>

Removing non-existing libraries using <rsel:exists /> is optional. To make it work, you have to add the following namespace to the Ant script’s root node:

<project name="Build specific targets and properties"
    xmlns:rsel="antlib:org.apache.tools.ant.types.resources.selectors">
    ...

Finally, the source code can be compiled:

<!-- compile scala source files -->
<mkdir dir="${target.folder}" />
<scalac srcdir="${source.folder1}"
    destdir="${target.folder}"
    classpath="${my.classpath}">
    <include name="**/*.scala" />
</scalac>

For production use, it is also a good idea to delete the source files from the target folder, so they are not deployed together with the binaries:

<!-- delete scala source files in output folder -->
<delete>
    <fileset dir="${target.folder}" includes="**/*.scala" />
</delete>

Conclusion

The method presented here uses the possibility to hook custom Ant targets into the PDE build process. The scripts try to find the Scala library and tools delivered with the IDE. If you don’t like that, you may also copy the files scala-library.jar and scala-compiler.jar (contains the Ant task) from the Scala distribution into some directory of your project and change the classpath respectively.

The complete source of the generic customBuildCallbacks.xml file can be downloaded with the following link:

customBuildCallbacks-old.xml (3.8 KiB)

This article relates to an old version of the Scala IDE for Eclipse. An updated Ant script can be found in a newer post!

This file can be copied without changes into every OSGi bundle that should be compiled with PDE and that contains Scala code. You only have to change the build.properties file as described above.