Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Ant's classpath in Eclipse

Tags:

java

eclipse

ant

I have an Ant build.xml file that works just fine on the command line: it compiles, builds the JAR, and I am able to execute the main method from the JAR just fine. The build.xml file references several thirdparty libraries that are scattered here and there. When building the JAR, the script doesn't include all the thirdparty libraries into the JAR itself. Instead, it puts their path into the JAR's manifest. This helps to keep my JAR slim and tidy.

I'd like to be able to edit and debug my project in Eclipse, but I can't find an easy way to do so. I can have my project use the Ant file to build the project, and that seems to work. However, Eclipse is having trouble finding the thirdparty libaries, and thus Eclipse is having two problems:

  1. it shows (in the text editor) lots of compile errors, because lots of classes are undefined, and
  2. it can't execute the JAR.

I can solve both of the above problems by specifying by hand, in two difference places (i.e., the build path via Properties->Java Build Path->Libraries, and the execution classpath via Run Configurations->Classpath), all the third party libraries. But it seems like I shouldn't have to do this manually, since all the third party libraries are already listed in my JAR's manifest. What am I doing wrong?

Here's my build.xml file:

<!-- Set global properties for this build -->
<property name="src"         location="./src" />
<property name="build"       location="./build"/>
<property name="dist"        location="./dist"/>
<property name="logs"        location="./logs"/>
<property name="docs"        location="./docs"/>
<property name="jar"         location="${dist}/dynamic_analyzer.jar"/>
<property name="lib"         location="../../thirdparty/lib"/>
<property name="hive-util"   location="../../hive-utils/dist"/>
<property name="hpdb"        location="../../hive-db/hpdb/dist"/>
<property name="static"      location="../../hive-backend/static_analyzer/dist"/>
<property name="mainclass"   value="com.datawarellc.main.DynamicMain"/>

<path id="dep.runtime">
    <fileset dir="${lib}"       includes="**/*.jar"/>
    <fileset dir="${hive-util}" includes="**/*.jar"/>
    <fileset dir="${hpdb}"      includes="**/*.jar"/>
    <fileset dir="${static}"    includes="**/*.jar"/>
</path>

<target name="clean">
    <delete dir="${build}"/>
    <delete dir="${dist}"/>
    <delete dir="${docs}"/>
    <delete dir="${logs}"/>
</target>

<target name="init">
    <tstamp/>
    <mkdir dir="${build}"/>
    <mkdir dir="${dist}"/>
    <mkdir dir="${logs}"/>
</target>

<target name="compile" depends="init">
    <javac srcdir="${src}" destdir="${build}" debug="on" includeantruntime="false">
        <classpath refid="dep.runtime" />
    </javac>

    <!-- Debug output of classpath -->
    <property name="myclasspath" refid="dep.runtime"/>
    <echo message="Classpath = ${myclasspath}"/>

</target>

<target name="jar" depends="compile">
    <!-- Put the classpath in the manifest -->
    <manifestclasspath property="manifest_cp" jarfile="${jar}" maxParentLevels="10">
        <classpath refid="dep.runtime" />
    </manifestclasspath>

    <jar jarfile="${jar}" basedir="${build}">
        <manifest>
            <attribute name="Main-Class" value="${mainclass}"/>
            <attribute name="Class-Path" value="${manifest_cp}"/>
        </manifest>
        <zipfileset dir="${src}" includes="**/*.xml" />
    </jar>
</target>

You can see that I have third-party libraries in several directories (${lib}, ${hive-util}, ${hpdb}, and ${static}). I use these to create a path called dep.runtime. I then include dep.runtime in the manifest when building my jar. How can I get Eclipse to use the same dep.runtime for the build path and the classpath when executing?

like image 622
stepthom Avatar asked Jun 04 '13 12:06

stepthom


2 Answers

An alternative to perl is to use an embedded groovy task:

<project name="demo" default="eclipse-files">

    <property name="src.dir"     location="src"/>
    <property name="classes.dir" location="build/classes"/>

    <path id="dep.runtime">
       <fileset dir="${lib}"       includes="**/*.jar"/>
       <fileset dir="${hive-util}" includes="**/*.jar"/>
       <fileset dir="${hpdb}"      includes="**/*.jar"/>
       <fileset dir="${static}"    includes="**/*.jar"/>
    </path>

    <target name="bootstrap">
        <mkdir dir="${user.home}/.ant/lib"/>
        <get dest="${user.home}/.ant/lib/groovy-all.jar" src="http://search.maven.org/remotecontent?filepath=org/codehaus/groovy/groovy-all/2.1.4/groovy-all-2.1.4.jar"/>
    </target>

    <target name="eclipse-files">
        <taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy"/>
        <groovy>
            import groovy.xml.MarkupBuilder

            project.log "Creating .classpath"

            new File(".classpath").withWriter { writer ->
                def xml = new MarkupBuilder(writer)

                xml.classpath() {
                    classpathentry(kind:"src",    path:properties["src.dir"])
                    classpathentry(kind:"output", path:properties["classes.dir"])
                    classpathentry(kind:"con",    path:"org.eclipse.jdt.launching.JRE_CONTAINER")

                    project.references."dep.runtime".each {
                        classpathentry(kind:"lib", path:it)
                    }
                }
            }
        </groovy>
    </target>

    <target name="clean">
        <delete file=".classpath"/>
    </target>

</project>

Notes:

  • The bootstrap target will download the 3rd party groovy jar (No dependency on perl)
  • Groovy can access the "dep.runtime" ANT path directly and iterate over its contents
  • Groovy has excellent support for writing XML files.

The following answer is similar and additionally generates the Eclipse .project file.

  • Using Apache Ivy with netbeans
like image 180
Mark O'Connor Avatar answered Oct 13 '22 01:10

Mark O'Connor


I came up with the following workaround, inspired by the link provided by @leeand00.

First, I wrote a simple Perl script (called genClasspath.pl) that generates the .classpath file that Eclipse uses.

#!/usr/bin/perl
use strict;

if (@ARGV != 2) {
  print STDERR "Usage: $0 OUTFILE CLASSPATHSTRING\n";
  print STDERR "e.g., $0 .classpath path1:path2:path3\n";
  exit 1;
}

my $OUTFILE         = $ARGV[0];
my $CLASSPATHSTRING = $ARGV[1];

open my $out_fh, '>', $OUTFILE or die "Couldn't open output file: $!";

print $out_fh q{<?xml version="1.0" encoding="UTF-8"?>
<classpath>
    <classpathentry kind="src" path="src"/>
    <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
    <classpathentry kind="output" path="build"/>
};

my @libs = split(":", $CLASSPATHSTRING);
foreach my $thisLib (@libs){
    print $out_fh "    <classpathentry kind=\"lib\" path=\"$thisLib\"/>\n";
}
print $out_fh "</classpath>\n";

Then, I have my build.xml file call this script with the content of dep.runtime:

<target name="compile" depends="init">
    <javac srcdir="${src}" destdir="${build}" debug="on" includeantruntime="false">
        <classpath refid="dep.runtime" />
    </javac>

    <property name="myclasspath" refid="dep.runtime"/>

    <exec dir="." executable="../../scripts/genClasspath.pl" os="Linux">
        <arg value=".classpath"/>
        <arg value="${myclasspath}"/>
    </exec>

</target>

The only catch is that I need to run Ant on the command line at least once before I open the project in Eclipse. But when I do, Eclipse is able to compile and execute my project just fine, since the classpath is exactly the same as Ant's.

like image 44
stepthom Avatar answered Oct 12 '22 23:10

stepthom