views:

573

answers:

2

I have a <path id="..."> in my build.xml. Before invoking the compiler I want to verify that every jar/directory on the classpath exists and print a warning with the missing ones. Does anybody know an existing solution or do I need to write my own task for that?


OK, I decided to go for a custom task. Here it is, in case anybody ever needs such a thing:

import java.io.File;
import java.util.Iterator;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.types.ResourceCollection;
import org.apache.tools.ant.types.resources.Resources;

public class CheckClasspathTask extends Task {

    private Reference reference;

    public CheckClasspathTask() {
    }

    public void setRefId(Reference reference) {
        this.reference = reference;
    }

    public void execute() throws BuildException {
        Resources resources = new Resources();
        resources.setProject(getProject());
        resources.add((ResourceCollection) reference.getReferencedObject());
        boolean isFirst = true;
        for (Iterator i = resources.iterator(); i.hasNext(); ) {
            String f = i.next().toString();
            if (!new File(f).exists()) {
                if (isFirst) {
                    isFirst = false;
                    System.out.println("WARNING: The following entries on your classpath do not exist:");
                }
                System.out.println(f);
            }
        }
    }
}
+1  A: 

I would say probably a custom ant task is in order, a little like the org.netbeans.modules.bpel.project.anttasks.ValidateBPELProjectTask done in a 'pre-dist' target of this build.xml.

Note: the ValidateBPELProjectTask ant task is a bit more complex than your usual custom task: it needs to have its own classpath to run (classpath not initially passed to the build.xml at first).
Since you cannot modify the classpath of the current ant task classloader, ValidateBPELProjectTask defines a new AntClassLoader and calls setContextClassLoader().

You will not need such a mechanism though: you can just pass the list of directories to check to your task as a parameter.

VonC
I seem to remember that passing a path object to a custom task is easy-- I assume that's what you meant by "pass the list of directories"?
araqnid
@araqnid yes, that is what I meant
VonC
+1  A: 

I haven't found many ways to iterate over a path in the Ant script using the default tasks. If you're building on a machine which has a UNIX-like shell, you can call out to the shell to check the classpath elements.

When calling shell scripts, you can use the apply task, but I couldn't get it to print when a classpath element didn't exist.

Let's assume you have the following classpath declaration:

<path id="your.classpath">
    <fileset dir="your.libs"/>
    <pathelement location="/some/missing/dir"/>
</path>

This will report if there are any missing elements, but doesn't tell you which ones:

<apply executable="test" type="file" ignoremissing="false">
    <arg value="-e"/>
    <srcfile/>
    <path refid="build.classpath"/>
</apply>

You could combine this with a simple shell script and get what you want by changing the executable attribute -- assuming you just want a big warning message and not a build failure:

#!/bin/sh

test -e "$1" || echo "WARNING: Classpath element $1 does not exist!"

If you want the build to fail, you can set the apply task to fail if an error is reported (a missing file/directory in this case) and then modify the shell script to return a non-zero exit code after the warning is printed.

An alternative would be to use the exec task and just execute a little script inline:

<pathconvert property="classpath" refid="build.classpath" pathsep=":"/>
<exec executable="sh">
    <arg value="-c"/>
    <arg value="for f in `echo ${classpath} | tr ':' '\n'`; do test -e $f || echo WARNING: Classpath element $f     does not exist!; done"/>
</exec>
Phil M
Unfortunately I cannot rely on having a Unix-like shell on every developer's box, so I have to stick to Java.However, your one-liner is very neat---worth of becoming a function in my ~/.bashrc+1
ngn