We all know this situation: the developers of a team are using the Eclipse IDE to code their apps, to compile their apps, to version, to deploy, and to run their apps. No problems so far. But as soon as you want to set up something like a continuous build system which runs outside of Eclipse you also need a build process that is independent of Eclipse. Usually you will use Ant and some other tools to define your automatic build process. Fine. But unfortunately Ant cannot use any of Eclipse's configurations, such as classpathes, launcher or team project sets. Although everything has been nicely configured already inside the IDE, it has to be redone in those external Ant build files. And even worse: it has to be reconfigured each time a developer makes changes in Eclipse. As always with redundant data, the challenge is to keep Eclipse and Ant configurations in sync. But how about avoiding redundancy in the first place? This is where ant4eclipse comes into play.
The aim of the ant4eclipse project is to avoid (or at least: to reduce) the redundancy of Eclipse and Ant configurations. More precisly: it consists of Ant tasks that are able to read and work with some of Eclipse's configuration files. With these tasks you are for example able to:
With all these tasks you're able to create a complete automatic build system that starts with checking out all required projects from CVS, builds all projects in the correct order with classpath settings as specified in Eclipse, and to launch your applications as they are specified in Eclipse. And the best of it: if you or someone else changes a configuration in Eclipse, those changes are immediately visible to your buildfiles - without changing one line of code!
Before you can use ant4eclipse within your ant files, you have to add the org.ant4eclipse_<version>.jar
JAR and all the JARs contained in the libs
directory of the ant4eclipse distribution to the ant4eclipse classpath. This can be done in any of the following ways (take a look at the Ant manual for further information):
org.ant4eclipse_<version>.jar
and the jars from the libs
directory from the ant4eclipse distribution into the lib
directory of your ant installation. An alternative approach is to copy the jar files to a directory called .ant/lib
in your home directory. Both directories will be searched by Ant for libraries.org.ant4eclipse_<version>.jar
and the jars from the libs
directory from the ant4eclipse distribution to the classpath by using Ant's "-lib"
command line argument:
ant -lib /path/to/ant4eclipse/ant4eclipse.jar -lib /path/to/ant4eclipse/libs <other ant args>;
After you have installed ant4eclipse, you can create a new build.xml
file and add the tasks that come with ant4eclipse using Ant's taskdef
task:
<project ... xmlns:ant4eclipse="antlib:org.ant4eclipse" xmlns:antcontrib="antlib:net.sf.antcontrib"> <taskdef uri="antlib:net.sf.antcontrib" resource="net/sf/antcontrib/antlib.xml" /> <taskdef uri="antlib:org.ant4eclipse" resource="org/ant4eclipse/antlib.xml" /> ... </project>
Note that you can not use the classpath
argument of the taskdef
task (otherwise the EJC compiler adapter won't work). You must make sure that ant4eclipse is included in ant's classpath as described above. If you want to edit your build files from Eclipse include the jars that comes with ant4eclipse in the Eclipse Ant runtime environment at Window -> Preferences -> Ant -> Runtime -> Global Entries. See the following screenshot for an example (Of course you have to adjust the path settings for your environment):
Ant4Eclipse provides a set of Ant-tasks that you can use to access and process your Eclipse project settings from your Ant buildfiles. Using ant4eclipse you can write build scripts that are driven by the configuration you made for your projects in Eclipse without to duplicate your configuration (like a projects classpath, its output- and source folders) in Eclipse and in Ant.
The Ant4Eclipse task can be grouped into three categories:
if
-task, that is part of the antcontrib project.
In additions to the tasks mentioned above ant4eclipse comes with a set of macros that provide out-of-the-box-solutions for some common tasks (like compiling a whole java project or building a plug-in). The macros can be found in several xml-files inside the "macros" subfolder in the ant4eclipse distribution. (Note: of course the macros are using the ant4eclipse tasks so they are a good demonstration of their usage).
Ant4Eclipse directly works with your project configuration thus eliminating the need of re-configuration your setup in your build file. Your Eclipse project setting completely drives your build. But there are some configurations that are not part of your project, but part of the workspace or Eclipse preferences. The JREs you define in Eclipse for example are stored in the .metadata
directory in your workspace. If you make a clean checkout of your project (for example in a nightly build), these informations are not available (to ant4eclipse) and need to be re-configured in ant4eclipse. Anyway that is pretty straight forward and we’ll show you in the following sections how you do this.
An Executor is a special kind of an Ant task. Executors "executing" a project artifact (like a Team Project Set, a Project, or a Launch configuration). Executing means that the executors parses the artifact and calls several callbacks targets, passing in the information just parsed (like the name of a project, it's classpath, it's source- and output folder, the main class to launch etc). The potential call back targets are executor-specific and are implemented in your build file as a sub element to the executor task.
The usage of Executors has several advantages including:
scoped
, that is they are only valid while the Executor resp. one of its call back targets is executed. Ant4eclipse makes sure that you always see what's relevant for your target. You don't need to manage the properties etc. yourself.Let's have a look at an example. Imagine you want to iterate over all projects contained in a Team Project Set (psf) file, for example to build those projects In the first step you have to iterate over all projects of that file. For this task ant4eclipse provides the executeProjectSet-Executor. This executor calculates the correct dependency hierarchy of the projects (for example according to their classpathes) and invokes the forEachProject
subtask for each project in the psf-file. In each iteration ant4eclipse passes some properties and references to the subtask describing the current project (for example it's name). The following snippet echoes the name of all project names in their correct build order:
<ant4eclipse:executeProjectSet workspace="c:/myworkspace" teamprojectset="my-project.psf" projectReferenceTypes="jdt"> <ant4eclipse:forEachProject> <!-- Note that ${executeProjectSet.project.name} is passed in by ant4eclipse --> <echo>Project that should be build: ${executeProjectSet.project.name}</echo> </ant4eclipse:forEachProject> </ant4eclipse:executeProjectSet>
It is even possible to use Executors within other Executors. We could enhance the previous example with the executeJdtProject-Executor, that works on an JDT-project and provides among others information about the classpath, source- and output-folders of a project. Let's dump the classpathes of all Projects:
<ant4eclipse:executeProjectSet workspace="c:/myworkspace" teamprojectset="my-project.psf" projectReferenceTypes="jdt"> <ant4eclipse:forEachProject> <!-- Note that ${executeProjectSet.project.name} is passed in by ant4eclipse --> <echo>Project that should be build: ${executeProjectSet.project.name}</echo> <ant4eclipse:executeJdtProject workspace="c:/myworkspace" projectName="${executeProjectSet.project.name}" <ant4eclipse:forProject> <echo>Classpath of project: ${executeJdtProject.classpath.absolute.compiletime}</echo> </ant4eclipse:forProject> </ant4eclipse:executeJdtProject> </ant4eclipse:forEachProject> </ant4eclipse:executeProjectSet>
Note that the allowed sub-tasks of an Executor can be used in any order and they are allowed to implement them as often as you need them. You could for example use the forEachOutputDirectory
sub-task of the executeJdtProject
-Executor at the beginning to clean the output directory before compiling. Then you use the forProject
sub-task to compile your project. In the last step you could again use forEachOutputDirectory
to instrument classes you just compiled:
<ant4eclipse:executeJdtProject workspace="c:/myworkspace" projectName="${executeProjectSet.project.name}"> <!-- 1. step: clean up all output directories --> <ant4eclipse:forEachOutputDirectory> ... </ant4eclipse:forEachOutputDirectory> <!-- 2. step: build the whole project --> <ant4eclipse:forProject> ... </ant4eclipse:forProject> <!-- 3. step: instrument the compiled classes --> <ant4eclipse:forEachOutputDirectory> ... </ant4eclipse:forEachOutputDirectory> </ant4eclipse:executeJdtProject>
Note: more real-world examples of Executors can be found in our ant4eclipse build-file.
We just have seen that some Executors iterate over a set of items, for example a list of projects or a list of output folders etc. Sometimes it's desired to filter such a list according to some criterias.
if
and unless
The easiest way to filter is to use the if
or unless
attributes on the sub-tasks of an executor. You can pass in a variable that expands to true
or false
. If the variable evaluates to true
the sub-tasks gets executed (if using the if
attribute) or is skipped (if using the unless
attribute) and vice versa. This is quite common to the if
and unless
attribute that some built-in ant tasks provides. In the following example the second forEachOutputDirectory
only gets invoked if the variable instrumentProjectClasses
is set to true
:
<ant4eclipse:executeJdtProject workspace="c:/myworkspace" projectName="${executeProjectSet.project.name}"> <!-- 1. step: clean up all output directories --> <ant4eclipse:forEachOutputDirectory> ... </ant4eclipse:forEachOutputDirectory> <!-- 2. step: build the whole project --> <ant4eclipse:forProject> ... </ant4eclipse:forProject> <!-- 3. step: instrument the compiled classes (only when 'instrumentProjectClasses' is set to 'true' --> <ant4eclipse:forEachOutputDirectory if="${instrumentProjectClasses}"> ... </ant4eclipse:forEachOutputDirectory> </ant4eclipse:executeJdtProject>
filter
While this approach allows you some simple filtering in some circumstances it's not enough. In the example above instrumentProjectClasses
is a regular static Ant variable. It's value doesn't change from one invocation of forEachOutputDirectory
to another. But what if you want to instrument the classes in the output directory depending on the name of the current (for example if you want to instrument only those directories whose name don't end with .test
)? For such cases ant4eclipse providers another, more powerful and flexible option: filters. All sub-tasks provide a filter
element that can be used to specify an LDAP-like query expression. Within this query you can access all scoped properties that are provided by the sub-task and it's parent Executor and compare them with your desired values.
Let's consider the example mentioned above, where you want to only instrument directories whose names not ending with .test
. In this case you could use the following filter:
<ant4eclipse:executeJdtProject workspace="c:/myworkspace" projectName="${executeProjectSet.project.name}"> <!-- 1. step: clean up all output directories --> <ant4eclipse:forEachOutputDirectory> ... </ant4eclipse:forEachOutputDirectory> <!-- 2. step: build the whole project --> <ant4eclipse:forProject> ... </ant4eclipse:forProject> <!-- 3. step: instrument the compiled classes (only when directory name doesn't end with '.test' --> <ant4eclipse:forEachOutputDirectory filter="(!(output.directory=*.text))"> ... </ant4eclipse:forEachOutputDirectory> </ant4eclipse:executeJdtProject>
Filter syntax
Filter expressions are based on the RCF 1960 "A String Representation of LDAP Search Filters". A filter is built of one ore more Criterias where each criteria defines a request to an attribute, that must be the name of a scoped property from the enclosing executor or it's sub-task, where you specify the filter. The specified value can contain an asterisk (*) to specify a wildcard that matches any set of characters. The following criterias are possible:
Several criterias can be combined using Operators. An operator is written before the criteria. Both, the operator and the criteria are surrounded by parenthesis. Ant4Eclipse supports the following operators:
(& (criteria-1)(criteria-2))
. It's even possible to specifiy more than two criterias: (& (criteria-1)(criteria-2)(criteria-n))
(| (criteria-1) (criteria-2)
. It's even possible to specifiy more than two criterias: (| (criteria1)(criteria2)(criteria-n)
(! (criteria-1)
Example
A filter that checks whether the scoped property project.name
starts with "org." or "net." would look like this:
(|(project.name=org.*)(project.name=net.*))
Since Eclipse is a platform that is not only used for developing Java applications but that works as a basis for development environments for several languages (for example C/C++, Python, PHP) and use cases (for example Web Development, Plug-in Development, Database development) it has an extensible concept of projects. There is not the one project type that is identical for all purposes. The most simple project type is the General Project that is defined in Eclipse' platform layer and that is more or less a "container" for files. A General Project doesn't know anything about programing languages, compilers etc. It just have a name and a location and it can be managed by the version control system features Eclipse provides (CVS, Subversion etc).
To support more than the General Project type, projects can get several natures assigned. A nature is a unique identifier (typically provided by a Plug-in) that is stored in the project's .project
file. Plug-ins (and Features) installed in Eclipse (for example the Java Tooling or the Plug-in dev. env) can access the natures of a project and can deceide if they need to participate in someway in that project. Some Plug-ins need more configuration options (like a classpath) that cannot be stored inside the .project
. Such Plug-ins typically work in addition with proprietary config files that are stored in the project (often as hidden files or directories starting with a .
). An example is the .classpath
file that specifies the Java classpath of a JDT/Java project. Of course it's possible that projects have more than one nature assigned. A Plug-in project from the Plug-in development environment (PDE) for example contains among the javanature
it's own org.eclipse.pde.PluginNature
.
Ant4Eclipse' tasks are grouped in layers that are related to a specific feature (as they are in Eclipse).
A general project is the most simple project type. It contains of a name, can contain natures and builders (in fact most simple projects don't have them since then they are not "general" anymore).
A General project can specify dependencies to other projects (even to projects of other types) (Project properties -> Project references). Note that this kind of projects reference differs from the JDT project references where you specify dependencies via the classpath. When you ask Ant4Eclipse for the dependency graph of your projecfts you can choose which type of dependencies you want to resolve.
Ant4eclipse' tasks that work even with General project are organized in the "platform" layer. Since all Eclipse project types "inherit" from General project, all tasks defined in the platform layer can be used with any other project type.
The ant task'getProjectDirectory
'
<pre> <ant4eclipse:getProjectDirectory workspacedirectory="${workspace}" projectname="simple.linked.project" property="test" /> </pre>
The ant condition 'hasNature
'
<pre> <antcontrib:if> <ant4eclipse:hasNature workspacedirectory="${workspace}" projectname="simple.java.project" nature="org.eclipse.jdt.core.javanature" /> <antcontrib:then> ... </antcontrib:then> </antcontrib:if> </pre>
To check whether a given eclipse contains a specific build command or not, you can use ant4eclipse's hasBuildCommand
condition.
<pre> <antcontrib:if> <ant4eclipse:hasBuildCommand workspacedirectory="${workspace}" projectname="simple.java.project" buildcommand="org.eclipse.jdt.core.javabuilder" /> <antcontrib:then> ... </antcontrib:then> </antcontrib:if> </pre>
executeBuildCommands
<pre> <ant4eclipse:executeBuildCommands workspacedirectory="${workspace}" projectname="simple.java.project"> <org.eclipse.jdt.core.javabuilder> ... </org.eclipse.jdt.core.javabuilder> </ant4eclipse:executeBuildCommands> </pre>
If you're working in Eclipse with Java projects, you work with JDT (Java Development Tools) projects. A project that is a JDT project has the org.eclipse.jdt.core.javanature
nature and contains usualy at least a org.eclipse.jdt.core.javabuilder
builder, that is responsible for (incrementally) building the project. Ant4Eclipse JDT-related tasks gives you access to all those JDT-specific settings.
Each JDT project declares a single classpath, that both works as a classpath when compiling your project ("compile-time classpath") and when you run it ("runtime classpath"). Both, compile and runtime classpath, are resolved from the same classpath definition that is stored in your projects .classpath
file. A JDT classpath definition contains a set of classpath entries. There are several kinds of entries that all can be used in a single classpath:
-classpath
argument with java
or javac
.An important ant4eclipse feature is to correctly resolve a classpath that has been defined in Eclipse. This way ant4eclipse is able to calculate the correct build order of a set of projects according to their dependencies.
When ant4eclipse resolves a classpath for you, it takes all configurations that are stored directly in your projects into account. But there are some configurations that are not part of your project. JRE definitions for example are stored in the .metadata
folder of your workspace, thus they are not available (for ant4eclipse) when you check your projects from your source code repository to a "clean" place where you want to start your build. The workspace settings have to be redefined in your build scripts. In the following sections we’ll show you how to do that. You will see that it’s quite easy.
Each Eclipse JDT-project usually contains an entry specifying the JRE or Execution Environment that should be used when compiling the project. This classpath entry is a "library" entry with a path starting with org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType
followed by the name of your JRE or Execution Environment. The name is specified in Eclipse when you register a JRE via Window -> Preferences -> Java -> Installed JREs or an Execution Environment (via Window -> Preferences -> Java -> Installed JREs -> Execution Environments):
If you have a JRE with the name "jdk_15" configured (as in the screenshot above) and have added it to your build path, the appropriate classpath entry would look like this:
<classpath> ... <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jdk_15"/> ... </classpath>
Unfortunatly the JRE definitions are not stored in your project, so ant4eclipse cannot access them. To make sure ant4eclipse can correctly resolve your JREs you must set them up in your build file using the installedJREs
type. This type contains a number of jre
sub-elements that take two attributes: an id
, that must match the name you have specified in Eclipse for a JRE. The second attribute, location
must point to the root directory of the matching JRE installation. To configure the "jdk_15" that is shown in the screenshot above, you have to add the following lines to your build file:
<ant4eclipse:installedJREs> <jre id="jdk_15" location="r:/jdk_15" /> </ant4eclipse:installedJREs>
If you have specified in your build an Execution Environment instead of a concrete JRE, ant4eclipse automatically discovers the best matching JRE out of all JREs you have specified with installedJREs
. If you want to force ant4eclipse to use a specific JRE for an Execution Environment, use the name of Execution Environment as the id attribute in the jre attribute.
Example:
If you're using the "J2SE-1.5" Execution Environment in your project, you're classpath contains the following entry:
<classpath> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/> </classpath>
jre
definition:<ant4eclipse:installedJREs> <jre id="J2SE-1.5" location="r:/jdk_15" /> </ant4eclipse:installedJREs>
As mentioned before, Eclipse allows you to add Classpath Libraries to your classpath. A Classpath Library is a set of files and directories that gets added to the classpath. There are two kind of libraries:
A classpath entry for a Classpath Library has set its kind
attribute to con ("Container"). Its path
attribute consists of the library type (a "User Library" for example is of type "org.eclipse.jdt.USER_LIBRARY") and it's name (last part of the path). If you use the JUnit library for JUnit4, in your project the classpath entry for this library would look like this:
<classpath> ... <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/> </classpath>
jdtClasspathLibrary
Ant type, that takes a set of resources that will be added to your classpath wherever you reference the library.jdtClasspathLibrary
The jdtClasspathLibrary
allows you to define a classpath library. The type expects the name of the library (that is the whole path
attribute of the classpath entry). To define the resources, that you want to add to your classpath whereever you use the library, use one or more resource collections. Please see Resource Collections in the Ant documentation for more informations on resource collections.
The following example declares the JUnit4 library with all jar-files that are located beneath c:\libs\junit4
:
<ant4eclipse:jdtClassPathLibrary name="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"> <!-- Define that resources that should be added to the classpath. Note that you can specify more than one resource collection element --> <fileset dir="c:/libs/junit4" includes="**/*.jar"/> </ant4eclipse:jdtClasspathLibrary>
Eclipse allows you to export (and import) your User Library definitions to an XML file. Ant4Eclipse is able to read this file and setup up the User Libraries for your build acordingly. In Eclipse you can export the library definitions via the User Libraries preference page (Preferences -> Java -> Build path -> User libraries -> Export...). The exported file can be read by the userLibraries
Ant type. The usage of this type is straightforward, simply pass in the path to your exported file. If you have exported your libraries to "c:\ant4eclipse\all.userlibraries" you would use the userLibraries
type this way:
<ant4eclipse:userLibraries userLibraries="c:/ant4eclipse/all.userlibraries"/>
While it's quite handy to use Classpath Libraries in Eclipse, it has some drawbacks when you implement a build, that propably run (automatically) outside of Eclipse:
Classpath Variables are similar to Classpath Libraries. A Classpath Variable is a single file or directory that will be added to your classpath. When the variable points to an external directory outside of your workspace, you must "extend" it when you add it to your classpath by adding a file from that directory to the classpath.
A classpath entry for a Classpath Variable has set its kind
attribute to var ("Variable"). Its path
attribute consists of the variable name (eg ANT4ECLIPSE_HOME
) and optionally the file that has been specified when adding it to the concrete classpath. For example:
<?xml version="1.0" encoding="UTF-8"?> <classpath> ... <classpathentry kind="var" path="ANT4ECLIPSE_HOME/libs/ant4eclipse.jar"/> ... </classpath>
To define the value of Classpath Variable in your build file you have to use the jdtClassPathVariable
type. The types expects two attributes: a name
, that must match the name of your Classpath Variable and a path
that must point to the path (file or directory) the variable should point to. To set the ANT4ECLIPSE_HOME
Classpath Variable to c:\ant4eclipse
, use the following code in your build file:
<ant4eclipse:jdtClassPathVariable name="ANT4ECLIPSE_HOME" path="c:/ant4eclipse"/>
jdtClasspathVariable
s in your build file. The declared variables are visible accross your whole build file.