Can I declare and initialize a variable in an Ant script? - java

In Ant, can I create a target that contains something like a variable that represents a path?
For example, something like the following pseudo target:
<target name="initPath">
Path = "${basedir}/../../myProject/Project/"
</target>
Where Path is my variable and is initializated to specific value.
How can I do this?

Ant build scripts are written in XML. To create a property has to be in XML style, so instead of this:
some_prop="some value"
It is this:
<property name="some_prop" value="some value"/>
Properties can contain periods, and I recommend using them as name separators:
<property name="some.prop" value="some value"/>
How do you declare a constant? Here:
<property name="some.prop" value="some value"/>
That's because once a property is set, it cannot be changed.
This way, you can do something like this:
<property file="${basedir}/build.properties"/>
<property name="some.prop" value="some value"/>
Let's say that the build.properties file contains this line:
some.prop="Some other value"/>
Now, when you run your Ant build file, the value of some.prop will be "Some other value", and the <property name="some.prop" value="some value"/> won't change it. I could even do this:
$ ant -Dsome.prop="A completely different value"
And this value of the some.prop property will override what I have in my build.properties file and what I have in my Ant build file.
This is a very nice feature. It allows me to set a default value that developers can override:
<property name="copy.verbose" value="false"/>
...
<copy todir="${copy.to.dir}"
verbose="${copy.verbose}">
<fileset dir="${copy.from.dir}"/>
</copy>
By default, when my copy task runs, it runs in non-verbose mode which is what I want. However, let's say I am having a few problems with my build, and I want to see exactly what is being copied, I could do this:
$ ant -Dcopy.verbose=true
And, now my copy tasks will show me all the files being copied.
A path is a way to declare something like $CLASSPATH or $PATH in the command line. You can predeclare a path with an id, and then use it later:
<javac destdir="${main.destdir}"
srcdir="${main.srcdir}">
<classpath>
<fileset dir="${lib.dir}">
<include name="*.jar"/>
</fileset>
</classpath>
</javac>
Here I'm adding a classpath. This is using <fileset/> to create a classpath based upon all of the jars in my ${lib.dir} directory.
I can also do this:
<path id="main.classpath">
<fileset dir="${lib.dir}">
<include name="*.jar"/>
</fileset>
</path>
<javac destdir="${main.destdir}"
srcdir="${main.srcdir}"
classpathref="main.classpath"/>
Here, I predeclare my main.classpath and then use that later in my <javac> task.
You should read up on Ant in the on line Ant Manual. There is a semi-decent introduction in the manual which might help clarify a few issues for you.

Here is how you can define a property in Ant script.
Unfortunately it is not a variable, since they are immutable. You can set a value to it, but you cannot change it during your script execution.
Here you can see an example of assigning value to a property.
Update.
You can use path task. For example:
<path id="combinedPath">
<path path="${toString:oldPath}"/>
<path path="my.jar"/>
</path>
<path id="reanamePath">
<path path="${toString:oldPath}"/>
</path>
If you change path in one target you can definitely access it in another one.

Ant has classpath-like support built-in.
In general you wouldn't use a property for this functionality.
Please consider reading the Ant documentation; this is Ant 101 and it'll be quicker in the long run if you spend some time with the docs and figuring out the Ant way of doing things.

Related

windows classpath issue with ant and java

I've been fighting in these trenches for a while now and have yet to get this working. Using many examples here on SO as well as others like this blog or this SO post, I still cannot get past the windows classpath limit. I'm currently just dealing with an ant task that runs a single main method in a java class within my source. If I can get this working here, I can extrapolate elsewhere.
First, my original relevant build tasks
<path id="code.classpath">
<path id="code.classpath">
<path refid="jars.code"/>
<path refid="jars.common"/>
<path refid="jars.servlet-api"/>
<dirset dir="${code.dir}" excludes="xlib/scripts/**"/>
</path>
<target name="code.exec" description="Execute a class file">
<echo>${code}</echo>
<input addproperty="code.exec.class">Enter full class name (e.g. ${atmr.pkg}.FooBar):</input>
<input addproperty="code.exec.args">Enter arguments:</input>
<java classname="${code.exec.class}"
fork="true"
dir="${code.src.dir}"
failonerror="true"
classpathref="code.classpath">
<jvmarg value="-Xmx4048M"/>
<jvmarg value="-ea"/>
<jvmarg value="-Dlog4j.configuration=file:${basedir}/log4j.xml"/>
<syspropertyset refid="proxy.properties"/>
<assertions refid="code.exec.assertions"/>
<arg line="${code.exec.args}"/>
</java>
</target>
Next, my most recent attempt at a solution
<path id="code.source">
<dirset dir="${code.dir}" excludes="xlib/scripts/**"/>
</path>
<target name="code.classpath.acme">
<manifestclasspath property="jar.classpath" jarfile="${app.dir}/acme.jar">
<classpath refid="code.source"/>
</manifestclasspath>
<jar destfile="${app.dir}/acme.jar" index="true">
<manifest>
<attribute name="Class-Path" value="${jar.classpath}"/>
</manifest>
</jar>
</target>
<path id="temp.classpath">
<pathelement path="${app.dir}/acme.jar"/>
</path>
<target name="code.exec" description="Execute a class file" >
<property name="myclasspath" refid="temp.classpath"/>
<echo>${code}</echo>
<echo>${basedir}</echo>
<echo>${myclasspath}</echo>
<input addproperty="code.exec.class">Enter full class name (e.g. ${atmr.pkg}.FooBar):</input>
<input addproperty="code.exec.args">Enter arguments:</input>
<java classname="${code.exec.class}"
fork="true"
dir="${code.src.dir}"
failonerror="true"
classpathref="temp.classpath">
<jvmarg value="-Xmx4048M"/>
<jvmarg value="-ea"/>
<jvmarg value="-Dlog4j.configuration=file:${basedir}/log4j.xml"/>
<syspropertyset refid="proxy.properties"/>
<assertions refid="code.exec.assertions"/>
<arg line="${code.exec.args}"/>
</java>
</target>
Basically, when running the first one, I get the "classpath is too long" issue in windows. Running the second one, I get "could not find or load main class package.classname." I've gone through the MANIFEST.MF that is created in the jar and the package location is present. From here went my travels down the rabbit hole to find what could be up. I've tested different permutations of the manifest relative locations to no avail. I've even gone in and manually changed the manifest to an explicit file location (not relative to the jar) with no change in results.
To confirm that the overall code works, I can add my code.dir path into temp.classpath and get the too long error. I can also make the acme jar build with the classes within it and get past the issue with the missing class.
Everywhere I read, this should work. Yet, here I am. I know that if the reference in the manifest can't be found, it silently skips it. That is what it seems to be doing, yet I've tried everything to get it to see what's there to no avail. And thus, I turn to you SO contributors to either point out the silly mistake I've missed, or let me in on the secret knowledge that I have yet to learn.
Thanks in advance.
You were perhaps closer with your original answer if you can at least achieve 'classpath is too long' answer.
Have you tried nesting your paths in order to make them 'relevant' rather than 'absolute' and thus shorter? perhaps using an alias such as the way you might list 'JAVA_HOME' in your PATH variable?
A way to test this quickly from the command line would be to edit the classpath and be sure to clear it out when you try to re-set it in code. In windows that would look like the following:
C:\WINDOWS>setx CLASSPATH "CLASSPATH;C:\some_new_path"
This will update the PATH by appending the new path to the existing path value. Typing the following command will print the new PATH in all future CMD windows; NOT in the current CMD window:
C:\WINDOWS>CLASSPATH
Typing the following will give you a list of all the environment variables:
C:\WINDOWS>set

Apache Ant {Compile javac srcdir} does not exist

I am new to apache ant and I am currently working on an apache Ant project. I Just started out, imported the project into workspace and tried to run the build.xml. I added all the libraries that come with the original project to the build path. I am having the following problem. Please someone else wrote the code and I am supposed to improve it. The directories this is all about exist in the project directory.
BUILD FAILED
C:\workspace\MyApp\build.xml:83: srcdir "C:\workspace\MyApp\${compile.javac.srcdir}" does not exist!
The error code is referencing the following part of the build.xml file
<target name="compile.default" depends="init">
<javac fork="yes" srcdir="${compile.javac.srcdir}" destdir="${compile.javac.destdir}" includes="${compile.javac.include}" excludes="${compile.javac.exclude}" classpath="${compile.javac.classpath}" debug="${compile.javac.debug}" optimize="${compile.javac.optimize}" deprecation="${compile.javac.deprecation}" verbose="${compile.javac.verbose}">
</javac>
<copy todir="${compile.javac.destdir}">
<fileset dir="${compile.javac.srcdir}" includes="${compile.copy.include}" excludes="${compile.copy.exclude}"/>
</copy>
</target>
<target name="compile" depends="init,compile.default" description="Compile all java source">
</target>
<!--+++++++++++++++-->
<!-- lib target(s) -->
<!--+++++++++++++++-->
<target name="lib.default" depends="init,compile">
<xmlbean schema="config/schemas/validate/1.0/validate.xsd" destfile="lib/glx-beans.jar" classpath="lib/xbean.jar:lib/jsr173_1.0_api.jar" />
<jar jarfile="${lib.filename}">
<fileset dir="${lib.srcdir}" excludes="${lib.exclude}" />
</jar>
</target>
<target name="lib" depends="init,compile,lib.default" description="Create all Project Libraries">
</target>
Would you please tell me what I am missing?
The ${compile.javac.srcdir} isn't defined. There are a few possibilities:
This is defined not in the build.xml, but in some sort of properties file. See if you have something like <property file="..."/> in your build script. My recommendation is to have all properties defined in the build.xml file, and use a properties file to override those settings. This way, the only build file that a developer needs in the build.xml file and doesn't have to worry about setting up a separate build.porperties file.
This is defined in the build.xml file under a particular task, but you forgot to say that your target where you use thisis dependent upon this task.
One of the things you can do is use the -d parameter when running Ant. I run the following command when running Ant with the -d parameter:
$ and -d 2>&1 | tee ant.out
I can then look at ant.out and see if somehow I didn't define that particular property. Maybe I had the wrong capitalization or misspelled the property name. For example, it's very likely I'll define the property as copmile.javac.srcdir because I don't know how to spell. Looking at the -d output can quickly point these types of errors out.
By the way, you shouldn't have all of your tasks dependent upon init since they're dependent upon compile.default anyway:
<target name="compile.default" depends="init">
....
</target>
<target name="compile" depends="compile.default">
....
</target>
<target name="lib" depends="compile,lib.default">
....
</target>
If I run the target lib, it will see compile is dependent upon compile.default which is dependent upon init. Thus, your build will run init, then compile.default, then compile, then 'lib.defaultand finallylib`.
If the init task is just setting up properties, you can do that outside of any task. Then, these properties will be setup before any task is executed. This way, they're not forgotten. If your init is also creating directories, you may want to move those <mkdir/> tasks in front of the task where that directory is used. For example, you may want to make the destdir uses in javac before the <javac/> task.
I find assigning default properties outside of any task, and creating directories before they are needed to simplify the build.xml. Plus, you're not creating a whole flock of unused directories if the user is merely compiling and not packaging the jar/war/etc.

ANT- Loading Multiple Java files from Several packages

I have several files scattered across several packages. I need to load the file one by one and perform operations using ANT but whenever I try, only one file gets loaded. For ex. I have 2 classes - com.abc.one.One and com.bcd.two.Two . The following script print both file name but only the first file as loaded file in both loop iterations
<target name="build" description="My Task">
<for param="file">
<path>
<fileset dir="C:\workspace\AntTest1" includes="**\*.java" />
</path>
<sequential>
<echo message="#{file}" />
<loadfile property="loadedFile" srcfile="#{file}" />
<echo message="${loadedFile}" />
</sequential>
</for>
</target>
I have tried searching the documentation but could not find the concise explanation on how to use loadfile task. I suspected that this might be because ant uses immutable string but could not get workaround. I tried to split the job by creating new target but that does not help me either. Any help is highly appreciated
Ant property can only be set once, and after it is set, it is immutable.
It has been some time since my Ant days, but perhaps the following solution can work: For each file, make an antcall call, with the file name as parameter. then, in the new target, load the file and perform your task. Notice that antcall can impact severely the runtime performance.

What is the difference between the <pathelement> attributes 'path' and 'location' in Ant?

I was running Selenium unit tests in TestNG with the Ant Java task like so:
<java classpathref="runtime.classpath"
classname="org.testng.TestNG"
failonerror="false">
<arg value="-d" />
<arg value="${grid.location}/target/reports" />
<arg value="${lib.location}/testng.xml"/>
</java>
runtime.classpath is a pathlike structure that included <pathelement path="${basedir}/target/classes/" />, which I thought was needed to let TestNG know which classes to run.
<path id="runtime.classpath">
...
<!-- Target classes -->
<pathelement path="${basedir}/target/classes/" />
</path>
However, I kept seeing in the log that TestNG found 0 applicable classes.
I eventually got some help from a colleague and it appears this was the key change:
<path id="runtime.classpath">
...
<!-- path attribute changed to location -->
<pathelement location="${basedir}/target/classes/" />
</path>
This also pulls in the test classes correctly:
<java classpathref="runtime.classpath"
classname="org.testng.TestNG"
failonerror="false">
<arg value="-d" />
<arg value="${grid.location}/target/reports" />
<arg value="${lib.location}/testng.xml"/>
<classpath>
<pathelement location="${basedir}/target/classes/" />
</classpath>
</java>
What is the difference between the path and location attributes? I've looked at Writing a Simple Buildfile (specifically the Path-like Structures section), but in that manual it looks to me like location is more specific than path. That doesn't appear to be the case empirically, but I can't quite figure out why.
It looks like the difference between path and location is many entries vs one. A location is a file or directory, a path can be a list.
From the manual
The location attribute specifies a single file or directory relative
to the project's base directory (or an absolute filename), while the
path attribute accepts colon- or semicolon-separated lists of
locations. The path attribute is intended to be used with predefined
paths - in any other case, multiple elements with location attributes
should be preferred.
Note that the JVM used by ant has just about no relation to the JVM used by the java task. By default the environment of ant isn't the same as that of things started with the java task via ant. This is actually helpful when you want to use a different JVM from the one ant wants to use and makes things explicit, helping avoid surprises later on.
Check out the docs for the java task, particularly clonevm
clonevm: If set to true, then all system properties and the
bootclasspath of the forked Java Virtual Machine will be the same as
those of the Java VM running Ant. Default is "false" (ignored if fork
is disabled). since Ant 1.7

Ant task for easier deployment

We have quite a big tree of source code, parts of it are deployed as two separate jar files. We need an easy control of what goes to which jar.
So far we do this by <exclude name="" /> and <include name="" /> tags, but this is quite inconvenient. The best option would be a separate config file with all the packages listed which we could comment out when needed, say with a '#' character.
Does something similar exist or do we have to write a new ant task that reads such a file and runs a <jar> task?
The best option would bee to seperate the code into different modules which can be build on their own (of course with dependencies to each other). Doing this also makes cyclic dependencies obvious and gives you the chance to optimize your code base.
ANT includes and excludes can be managed with external files and referenced in a fileset using includesfile and excludesfile attributes.
includesfile the name of a file; each
line of this file is taken to be an
include pattern.
excludesfile the
name of a file; each line of this file
is taken to be an exclude pattern.
For example:
<jar destfile="${dist}/lib/app1.jar">
<fileset dir=".">
<includesfile name="app1.properties"/>
</fileset>
</jar>
<jar destfile="${dist}/lib/app2.jar">
<fileset dir=".">
<includesfile name="app2.properties"/>
</fileset>
</jar>

Categories

Resources