I'm new to Ant.
Can someone please tell me what value to put for the 'classpathref' for taskdef?
Will it be the path of the class file?
If yes can an example be given because I tried that and its not working for me.
In the taskdef, the classpathref should be a reference to a previously defined path.
The path should include a jar archive that holds the class implementing the task,
or it should point to the directory in the file system that is the root of the class hierarchy.
This would not be the actual directory that holds your class if your class resides in a package.
Here's an example.
MyTask.java:
package com.x.y.z;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
public class MyTask extends Task
{
// The method executing the task
public void execute() throws BuildException {
System.out.println( "MyTask is running" );
}
}
Note that the package name is com.x.y.z, so when deployed -
lets say the classes are put under a directory called classes - we might see the class here in the file system:
$ ls classes/com/x/y/z
MyTask.class
Here's a simple build.xml that uses the task:
<project name="MyProject" basedir=".">
<path id="my.classes">
<pathelement path="${basedir}/classes" />
</path>
<taskdef name="mytask" classpathref="my.classes" classname="com.x.y.z.MyTask"/>
<mytask />
</project>
Note that the classpathref given points at the classes directory - the root of the class hierarchy.
When run, we get:
$ ant
Buildfile: .../build.xml
[mytask] MyTask is running
You can do similar using an explicit classpath, rather than a 'classpathref', for example:
<property name="my.classes" value="${basedir}/classes" />
<taskdef name="mytask" classpath="${my.classes}" classname="com.x.y.z.MyTask"/>
Related
I have an ant file called jarPLCExample.xml that takes some class files and produces a jar file. What would the <jar> tag section of this file look like in sbt?
<project name="jar_myplc" default="jar_myplc" basedir=".">
<property file="../resources/v2.properties"/>
<property name="dist.dir" value="C://where_jar_files_go/lib"/>
<property name="dist.file" value="${dist.dir}/PLC.jar"/>
<target name="jar_myplc">
<tstamp>
<format property="TODAY" pattern="dd/MM/yyyy hh:mm aa" locale="au"/>
</tstamp>
<delete file="${dist.file}" quiet="true"/>
<jar destfile="${dist.file}">
<fileset dir="${v2.out.root}" includes="com/companyname/server/applic/**/*.class"/>
<fileset dir="${v2.out.root}" includes="com/companyname/common/utils/SeaDef*.class"/>
</jar>
<copy file="${dist.file}" todir="C:/deploy"/>
</target>
sbt-assembly no good for this as it seems you can exclude and include jars, but not source file packages and source file names as needs to be done here.
Obviously important to 'go with the flow': I expect sbt will want to compile from source, so no need to explicitly use .class files as the ant task above does.
I already have a build.sbt in the base directory, and it would be sensible to call this file jarPLCExample.sbt, but I don't know how to get sbt to load one particular file. So instead I will have two projects in the one file and manually set the current project by changing their order. (As they both have the same key the second one seems to overwrite the first one - the projects command will always show only one project).
This answer has two shortcomings. It only includes the applic directory rather than all directories (recursively) below applic. Also it will pick up all SeaDef*.class, no matter which directories they are in.
def filter3(file: File, greatGrandParent:String, grandParent:String, parent:String, excludes:List[String]):Boolean = {
file.getParentFile.getName == parent &&
file.getParentFile.getParentFile.getName == grandParent &&
file.getParentFile.getParentFile.getParentFile.getName == greatGrandParent &&
!excludes.exists( _ == file.getName)
}
/*
* <fileset dir="${v2.out.root}" includes="com/companyname/server/applic/**/*.class"/>
* <fileset dir="${v2.out.root}" includes="com/companyname/common/utils/SeaDef*.class"/>
*/
lazy val jarPLCExample = project.in(file(".")).
settings(commonSettings: _*).
settings(
includeFilter in (Compile, unmanagedSources) :=
new SimpleFileFilter(file => filter3(file, "companyname", "server", "applic", List())) ||
new SimpleFileFilter(file => file.getName.startsWith("SeaDef"))
)
I am implementing an ant task as a wrapper for another class, which loads other several classes using the system class loader. Now, the task is in the same jar of these other classes, so I wonder why it is not finding them, since the task is running
Please notice that my classes are in the $CLASSPATH env variable. The problem will not occur if I export LOCALCLASSPATH=$CLASSPATH
Minimal example:
<taskdef name="mytask" classname="my.package.MyTask" />
<target name="compile">
<mytask />
</target>
you can easily see the problem here
public class MyTask extends Task {
public void execute() throws BuildException {
try {
ClassLoader cl = ClassLoader.getSystemClassLoader();
// this will only print the ant jar file path
for(URL url: ((URLClassLoader)cl).getURLs()){
log(url.getFile());
}
cl.loadClass("my.package.OtherClass"); // throws an exception
} catch (Exception ex) {
throw new BuildException(ex);
}
}
}
The ant shell script reworks the classpath internally, so the SystemClassLoader contains only a minimal part of the "real" classpath
tl;dr: use
ClassLoader cl = Thread.currentThread().getContextClassLoader();
instead of
ClassLoader cl = ClassLoader.getSystemClassLoader();
From the Mailing List, Rainer Noack:
if you're launching ant via shell script, it is using
oata.launcher.Launcher.java
This class reorganises the classpath a bit. The env variable CLASSPATH
and the classpath commandline argument are stripped and replaced by
the minimum classpath used to launch ant. A child classloader of the
system classloader is created with the original CLASSPATH entries.
The oata.Project is then loaded with this classloader.
The problem is that the loader in ClassLoader.getSystemClassLoader() is actually untouched, the one that changes (i.e., the one that honors $CLASSPATH, -lib, etc.) can be retrieved using Thread.currentThread().getContextClassLoader().
If I pass a variable to ant by doing
ant -Dsomething=blah
How can I refer to it in my build.xml? I tried #something# and ${something} but neither seem to work.
Ultimately what I am trying to do is set some properties (versions) at compile time.
update: the problem of course turned out to be somewhere else - accepting the most complete looking answer with examples
Don't you hate it when you over think these things:
<project name="test">
<echo message="The value of foo is ${foo}"/>
</project>
Now, I'll run my program. Notice that I never defined a value for property foo in my build.xml. Instead, I'll get it from the command line:
$ ant -Dfoo=BAR_BAR_FOO
test:
[echo] The value of foo is BAR_BAR_FOO
BUILD SUCCESSFUL
time: 0 seconds
See. Nothing special at all. You treat properties set on the command line just like normal properties.
Here's where the fun comes in. Notice that I've defined the property foo in my build.xml this time around:
<project name="test">
<property name="foo" value="barfu"/>
<echo message="The value of foo is ${foo}"/>
</project>
Now watch the fun:
$ ant
test:
[echo] The value of foo is barfu
BUILD SUCCESSFUL
time: 0 seconds
Now, we'll set the property foo on the command line:
$ ant -Dfoo=BAR_BAR_FOO
test:
[echo] The value of foo is BAR_BAR_FOO
BUILD SUCCESSFUL
time: 0 seconds
See the command line overrides the value I set in the build.xml file itself. This way, you can have defaults that can be overwritten by the command line parameters.
It sounds like you want to do something like the following:
<mkdir dir="build/src"/>
<copy todir="build/src" overwrite="true">
<fileset dir="src" includes="**/*.java"/>
<filterset>
<filter token="VERSION" value="${version}"/>
</filterset>
</copy>
...which will cause your source to get copied, replacing #VERSION#:
public class a { public static final String VERSION = "#VERSION#"; }
...and then include build/src in your javac src.
That said, I don't recommend this approach since the source copy step is expensive, and it will undoubtedly cause confusion. In the past, I've stored a version.properties file in my package with version=x.y. In my Java code, I used Class.getResourceAsStream("version.properties") and java.util.Properties. In my build.xml, I used <property file="my/pkg/version.properties"/> so that I could create an output-${version}.jar.
${argsOne} works for me and is easily referenced if the invoking command is
ant -DargsOne=cmd_line_argument
Ant documentation also says so. This should work, try running with ant -debug and paste the output.
I'm in the process of modifying an Ant script (currently in use from within MyEclipse) to work from the command line. I'm doing this so anyone can check out the project and build it without MyEclipse. The problem I'm running into is that MyEclipse includes the dependencies behind the scenes. It does this by looking at the workspace's Ant configuration and compiling the classpath based on the selected libraries in the preferences dialog. Long story short, I need to take those dependencies and make the script smart enough to include them on its own, without the help of MyEclipse.
The tasks that are giving me a headache are the sshexec and scp tasks. They are optional ant tasks that require a version of jsch to run. I removed jsch from MyEclipse's Ant classpath and added it to a lib folder in the project itself (lib/dev). MyEclipse immediately complained that the SSHExec class could not find the dependent class, com.jcraft.jsch.UserInfo which is part of jsch-0.1.44.jar.
I don't see a way to set the classpath for Ant from within the build script. I have the following code, which adds a path element to the script, but I don't think Ant uses this unless explicitly associated to a task or another element.
<path id="web-jars">
<fileset dir="${web-lib}">
<include name="**/*.jar" />
</fileset>
<fileset dir="${app-lib}"> <!-- this is where jsch resides -->
<include name="**/*.jar" />
</fileset>
</path>
It seems that I need to use taskdef to define the sshexec and scp tasks:
<taskdef name="sshexec" classname="org.apache.tools.ant.taskdefs.optional.ssh.SSHExec"
classpathref="web-jars"/>
MyEclipse complains about this, "taskdef A class needed by class org.apache.tools.ant.taskdefs.optional.ssh.SSHExec cannot be found: com/jcraft/jsch/UserInfo"
It's clearly in the classpathref, web-jars. And I can't run anything in the script because of this malformed or misconfigured taskdef.
The problem here is that the SSHExec class is loaded from a classloader which itself has no access to your web-jars class loader. Supplying this classpath for the taskdef does not change this. Each class can only load classes from its own classloader and any parent class loaders, but the web-jars classloader is not a parent class loader of SSHExec's class loader (it is likely the other way around, since SSHExec seems to be found here).
It looks like this:
ClassLoader web-jars -------------> application CL -------------> bootstrap CL
taskdef
=> look for SSHExec here
=> look first in parent class loader
=> look for SSHExec here
=> look first in parent class loader
=> look for SSHExec here
=> not found
=> look in our own classpath
=> found, load the class
=> it somehow uses interface UserInfo
=> look for UserInfo here
=> look first in parent class loader
=> look for UserInfo here
=> not found
=> look in our own classpath
=> not found, throw exception.
The VM has no idea to look for UserInfo (and the other JSch classes) in the web-jars classloader.
I suppose the SSHExec task is somewhere in the usual ant classpath, i.e. loaded by the application class loader. Then removing SSHExec from ant's classpath (or adding jsch.jar to it) seems to be the only solution here.
Create ~/.ant/lib and copy jsch.jar in there as part of the build initialisation. Any tasks which do scp/sshexec work should depend on this init target.
<target name="init">
<property name="user.ant.lib" location="${user.home}/.ant/lib"/>
<mkdir dir="${user.ant.lib}"/>
<copy todir="${user.ant.lib}">
<fileset dir="${basedir}/build/tools" includes="jsch-*.jar"/>
</copy>
</target>
<target name="mytarget" depends="init">
<scp todir="user#host"><fileset dir="..."/></scp>
</target>
The Ant within Eclipse unfortunately won't pick this up immediately as it does not read ~/.ant/lib on every execution; After running mytarget within Eclipse once and watching it fail, then go to:
Window>Preferences>Ant>Runtime and press Restore Defaults - this will add any .jar files from ~/.ant/lib to the Global Entries section and you should be good to go.
I have a build script and as part of that script it copies a jar file to a directory, for ease lets call it the utils jar. the utils jar is built by another build script sitting in another directory. What im trying to do have my build script run the utils build script so that I can ensure the utils jar is up to date.
So I know I need to import the utils build file.
<import file="../utils/build/build.xml" />
Which doesn't work because the import task, unlike almost every other ant taks, doesn't run from basedir, it runs from the pwd. So to get around that I have this little ditty, which does successfully import the build file
<property name="baseDirUpOne" location=".." />
<import file="${baseDirUpOne}/utils/build/build.xml" />
So now that ive solved my import problem I need to call the task, well that should be easy right:
<antcall target="utils.package" />
note that in the above, utils is the project name of ../utils/build/build.xml
the problem I'm now running into is that ant call doesn't execute in ../utils/build so what I need, and cant find, is a runat property or something similar, essentially:
<antcall target="utils.package" runat="../utils/build" />
The reason I need this is that in my utils build file the step to select which code to copy to the jar is based on relative paths so as to avoid hardcoding paths in my ant file. Any ideas?
I've got something similar set up: I have a main Ant build.xml which calls a separate build.xml that takes care of building my tests. This is how I do it:
<target name="build-tests">
<subant target="build">
<fileset dir="${test.home}" includes="build.xml"/>
</subant>
</target>
The trick is to use subant instead of antcall. You don't have to import the other build file.
Try using the "ant" task instead of the "antcall" task, which runs the imported build directly instead of importing it into the current build file. It has a "dir" parameter:
the directory to use as a basedir
for the new Ant project. Defaults to
the current project's basedir, unless
inheritall has been set to false, in
which case it doesn't have a default
value. This will override the basedir
setting of the called project.
So you could do:
<ant antfile="${baseDirUpOne}/utils/build/build.xml" dir="../utils/build" />
or something like that.
You can pass params down to antcall using nested in the antcall block. So, you can pass the properties down that way (probably even basedir since properties are immutable).