Conditionally Delete in Ant - java

I want to delete the directory if the property "delete-compiled-dir" is set to true. If the property is false then do not execute it. Right now I have
<target name="deleted-after-compilation" depends="compile,jar">
<condition property="${delete-compiled-dir}" value="true">
<delete dir="${compilation-dir}" />
</condition>
<echo> Deleting Compiled Directory Classes </echo>
</target>
I get the error message :
condition doesn't support the nested "delete" element.

You can add the condition to your target using if (see manual).
The target will only be executed when the property compilation-dir is set (to any value, e.g. false).
<target name="deleted-after-compilation" depends="compile,jar"
if="${compilation-dir}">
<delete dir="${compilation-dir}" />
<echo> Deleting Compiled Directory Classes </echo>
</target>
To only execute it when a property is set to true, you need to set another property first and check this one in the if. You could add both as dependency the another target:
<target name="set-delete-property">
<condition property="delete-compilation-dir">
<istrue value="${compilation-dir}"/>
</condition>
</target>
<target name="deleted-after-compilation"
depends="compile,jar" if="${compilation-dir}">
....
<target name="some-target"
depends="set-delete-property,deleted-after-compilation">
</target>

There are a few ways to do this:
Use conditions on the target entity
Targets can contain if and unless conditions. The target will execute depending whether or not the property is set. (Not set to true, just set). This is a common way to see if you need to do something or not:
<target name="deleted.after.compilation"
if="delete.compiled.dir"
depends="jar">
<delete dir="${compilation-dir}" />
<echo> Deleting Compiled Directory Classes </echo>
</target>
You can set the property on the command line:
$ ant -Ddelete.compiled.dir all
Note: I use periods as separators for the names of properties and targets. Also note that I only depend upon the target jar since jar is also dependent upon compile, there's no need to have them both.
Use Ant's 1.9.1 conditional clauses
As of Ant 1.9.1, Ant has conditional attributes that can be added to tasks. You need to add a Namepsace declaration in your <project> entity:
<project ...
xmlns:if="ant:if"
xmlns:unless="ant:unless">
<target name="deleted.after.compilation"
depends="jar">
<delete dir="${compilation-dir}" if:true="${delete.compiled.dir}"/>
<echo if:true="${delete.compiled.dir}"> Deleting Compiled Directory Classes </echo>
</target>
Use Ant-Contrib's If Statement
Ha ha, that wacky Ant-Contrib library. No one knows who maintains it, and it hasn't been touched in years, but many people depend so heavily on it.
<project ...>
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<fileset dir="${ivy.dir}/antcontrib">
<include name="ant-contrib*.jar"/>
</fileset>
</classpath>
</taskdef>
<target name="deleted.after.compilation"
depends="jar">
<if>
<istrue value="${delete.compiled.dir}"/>
<then>
<delete dir="${compilation-dir}"/>
<echo>Deleting Compiled Directory Classes </echo>
</then?
</if>
</target>
You can see why Ant-Contrib is popular. It contains a lot of power, and we all know it. Plus, if someone is still using Ant 1.8 or 1.7, this will still work.

if you have get the property ,you can just use it in a target.
<target name="delete" if="${delete-compiled-dir}">
<delete dir="${compilation-dir}" />
</target>

As of Ant 1.9.1, you can use conditionals on any task. Described here.
Add this namespace to your project element:
<project name="yourproject" xmlns:if="ant:if">
Then add this to your delete:
<delete dir="${compilation-dir}" if:true="${delete-compiled-dir}"/>

Related

Problems with Ant build.xml configuration to work with external Libraries and Java property files

I have a problem with Ant Build Tool.
First, below you can see my project structure:
and the content of my build.xml file is:
<?xml version="1.0" encoding="UTF-8"?>
<project name="addonGenerator" default="main" basedir=".">
<property name="projectName" value="addonGenerator"/>
<property name="src.dir" location="src"/>
<property name="build.dir" location="bin"/>
<property name="dist.dir" location="dist"/>
<target name="compile" description="compile the source ">
<mkdir dir="${build.dir}"/>
<javac srcdir="${src.dir}" destdir="${build.dir}">
<classpath>
<pathelement path="lib/velocity-1.7.jar"/>
<pathelement path="lib/log4j-1.2.16.jar"/>
</classpath>
</javac>
</target>
<target name="dist" description="package, output to JAR">
<mkdir dir="${dist.dir}"/>
<jar jarfile="${dist.dir}/${projectName}.jar" basedir="${build.dir}">
<zipgroupfileset dir="lib" includes="velocity-1.7.jar" />
<zipgroupfileset dir="lib" includes="log4j-1.2.16.jar" />
<manifest>
<attribute name="${projectName}" value="main"/>
<attribute name="Main-Class" value="main.java.AddonGenerator"/>
</manifest>
</jar>
</target>
<target name="clean" description="clean up">
<delete dir="${build.dir}"/>
<delete dir="${dist.dir}"/>
</target>
<target name="main" depends="clean, compile, dist"/>
</project>
I don't know how setup the Ant build.xml to build and run my project with external libraries and the java property file generator.properties
To include your generator.properties file in the .jar file, add your resources directory when building the .jar:
<jar jarfile="${dist.dir}/${projectName}.jar" basedir="${build.dir}">
<fileset dir="src/main/java/resources"/>
Since you are currently building a “fat jar” (by directly including the contents of your library .jars in your application .jar), you can run by simply invoking your .jar file. Such a target obviously requires the .jar file to be built, so it makes sense to depend on the "dist" target:
<target name="run" depends="dist">
<java jar="${dist.dir}/${projectName}.jar"/>
</target>
On another note, I don’t think you want to pass src as your source directory, unless your classes actually declare themselves with ‘package main.java;’ (which they shouldn’t). You should pass the actual root of your packages to the javac task:
<property name="src.dir" location="src/main/java"/>
You should also make the "dist" target depend on "compile", since, well, it depends on having compiled classes available.
I also would suggest that your default target, "main", avoid calling the "clean" target. You should not clean before every single build; that defeats one of the most useful benefits of Ant, namely the ability to update only the things that need to be updated. You should only clean when you need to, with a command like ant clean compile or simply ant clean.
Note that once "dist" depends on "compile", and once "main" no longer calls "clean", you can simply remove the "main" target and change your project’s default target to "dist". When you think about it, this makes sense: the default action is to build and package the application.

Working with ANT and facing an issue

<target name="init">
<mkdir dir="${build.dir}" />
<if>
<available file="../war" type="dir"/>
<then></then>
<else>
<mkdir dir="../war" />
</else>
</if>
</target>
This is the code i am using to check if a folder exists, but getting the following error:
Cause: The name is undefined.
Action: Check the spelling.
Action: Check that any custom tasks/types have been declared.
Action: Check that any / declarations have taken place.
I have copied ant-contrib.jar in ANT_HOME/lib. where am i goin wrong?
Given the example above, you can greatly simplify it:
<target name="init">
<mkdir dir="${build.dir}" />
<mkdir dir="../war" />
</target>
...since the mkdir task does nothing if the folder exists (see documentation).
If you're asking how to use if and then in ant, I recommend picking another example since each action in Ant tends to have its own conditionals built in.

How can I build multiple projects in Ant with one build file?

Can I build multiple projects from one build-file. Example:
<project basedir="." default="all" name="app1">
...
</project>
<project basedir="." default="all" name="app2">
...
</project>
Currently I type ant -f build1.xml compile and it builds my application and I have to use two separate build files. Is there some way to get it running in a way that i have both the projects defined a common build-file and I can type something like ant app1 compile or ant app2 compile?
Here's what my build-file looks like:
<?xml version="1.0" encoding="UTF-8"?>
<project name="azebooster" default="dist" basedir=".">
<!-- Globals -->
<property name="src" location="src/com/aelitis"/>
<property name="build" location="build/azebooster"/>
<property name="jar" location="jar/azebooster"/>
<property name="resources" location="res/azebooster"/>
<!-- Paths -->
<path id="classpath">
<fileset dir="." includes="**/*.jar"/>
</path>
<!-- Start it -->
<target name="init">
<tstamp/>
<mkdir dir="${build}"/>
<mkdir dir="${jar}"/>
</target>
<!-- Build it -->
<target name="compile" depends="init" description="compile the source" >
<javac srcdir="${src}" destdir="${build}">
<classpath>
<path refid="classpath"/>
</classpath>
</javac>
</target>
<!-- Jar it -->
<target name="jar" depends="compile">
<jar destfile="${jar}/${ant.project.name}.jar">
<fileset dir="${build}"/>
<fileset dir="${resources}" />
</jar>
</target>
<!-- Clean it -->
<target name="clean" description="clean up" >
<tstamp/>
<delete dir="${build}"/>
<delete dir="${jar}"/>
</target>
</project>
Thank you.
Yes you can create a default.build file (in this way you don't need to specify the file, because it's used by default). On it you can create the following targets:
<target name="all" depends="app1, app2" />
<target name="app1">
<ant antfile=<app1file> target="compile" />
</target>
<target name="app2">
<ant antfile=<app2file> target="compile" />
</target>
On this way you can use both ant file from one unique file.
And you can do all in only one file, if you replace the app1 and app2 targets, with the needed targets to compile them that you have in the separate files.
EDITED:
You have different ways to include both of them in only one file, maybe the easiest one is include a suffix for each project on each target. And you can call the specific project target or the target for both.
I put you an example with the compile target (and for init target too).
You can use compile for compile both projects (I call the other project other), and compileazeboster to compile the azeboster project.
You can search the common things to avoid the innecesary duplicated code (common paths, targets and so on)
<property name="srcazebooster" location="src/com/aelitis"/>
<property name="buildazebooster" location="build/azebooster"/>
<property name="jarazebooster" location="jar/azebooster"/>
<property name="srcother" location="src/other"/>
<property name="buildother" location="build/other"/>
<property name="jarother" location="jar/other"/>
<!-- Start it -->
<target name="init" depends="initazebooster, initother"/>
<!-- Build it -->
<target name="compile" depends="compileazebooster, compileother" description="compile the source for all" />
<!-- Start azebooster-->
<target name="initazebooster">
<tstamp/>
<mkdir dir="${buildazebooster}"/>
<mkdir dir="${jarazebooster}"/>
</target>
<!-- Build azeboster-->
<target name="compileazebooster" depends="initazebooster" description="compile the source for azebooster" >
<javac srcdir="${srcazebooster}" destdir="${buildazebooster}">
<classpath>
<path refid="classpath"/>
</classpath>
</javac>
</target>
<!-- Start other-->
<target name="initother">
<tstamp/>
<mkdir dir="${buildotherr}"/>
<mkdir dir="${jarother}"/>
</target>
<!-- Build other-->
<target name="compileother" depends="initother" description="compile the source for other" >
<javac srcdir="${srcother}" destdir="${buildother}">
<classpath>
<path refid="classpath"/>
</classpath>
</javac>
</target>
You will probably be able to do what you want using macros (though it depends precisely on what is common between your two projects), though your command is more likely to be
ant compile app1
or
ant compile app2
where compile is a target which calls a macro, using app1/app2 as a parameter to either decide which macro to call or to pass into the macro itself.
I would like to add to the excellent answer of Borja some important steps.
How to organize the common ant files?
Let's say we have some big Project consisting of many Eclipse projects. All of them are in a worspace folder. Thus, the workspace itself makes the global Project. (Notice P/p difference). Very often you already have an Ant build file in the workspace folder. Or you have created the common ant files (build and properties) in it by yourself.
Later you want to launch that global build file. How? Do you want to go into the workspace folder, change to the command line or shell window and run the ant from there, having the output in the external console and switching from Eclipse to that window and back? And the same problem for editing? Having two additional windows? No, surely you want to have all windows, editing and output, in Eclipse. But how can we reference those global build files from inside the Eclipse?
Go to Package Explorer.
Make an empty project(not Java one), let's name it _Global (we want to see it always on the top).
Right click on its name. Choose Import -> General -> File System.
Press Advanced. Check Create links in Workspace, Create virtual folders, create link locations. The last choose for WORKSPACE_LOC.
In From Directory go to the workspace or where you have created the common build. After all, it could be even the common one for several workspaces.
You will see a dialogue with file names on the right with checkfields. Check the files you need. (You will need to import every new common file you created thereafter).
Finish.
Now you see your project with references to the global files. As for properties files, you are ready. But for build files you need some more steps:
Right click your global build file, -> Run -> Run Configurations. You are in the Ant Build group of configurations.
Press the New Launch Configuration button with plus on it (above the launch configurations list). Now you have the run configuration for that global build.
Set its parameters and run/debug it.

Ant: How to know if at least one copy task from ten copy tasks actually copied a file

I have a target which has several copy tasks; It basically copies the common jars to our set of applications from a centralized location to the lib folder of the application.
Seeing as this is a regular copy task a jar will be copied only if it is newer than the one currently in the lib folder.
This is the relevant part in the build.xml:
<target name="libs"/>
<copy file=... />
<copy file=... />
<copy file=... />
<antcall target="clean_compiled_classes"/>
</target>
<target name="clean_compiled_classes" if="anyOfTheLibsWereCopied">
<delete .../>
</target>
I'm looking for a way to set the anyOfTheLibsWereCopied property before the ant call in the libs target based on whether or not any of the files has been actually changed.
Thanks,
Ittai
I would advise having a look at the Uptodate task. I have never used it before but I guess what you are trying to do will be implemented along the following lines:
<target name="libs"/>
<uptodate property="isUpToDate">
<srcfiles dir="${source.dir}" includes="**/*.jar"/>
<globmapper from="${source.dir}/*.jar" to="${destination.dir}/*.jar"/>
</uptodate>
<!-- tasks below will only be executed if
there were libs that needed an update -->
<antcall target="copy_libs"/>
<antcall target="clean_compiled_classes"/>
</target>
<target name="copy_libs" unless="isUpToDate">
<copy file=... />
<copy file=... />
<copy file=... />
</target>
<target name="clean_compiled_classes" unless="isUpToDate">
<delete .../>
</target>
Your other option would be to implement your own ant task that does what you want. This would require a bit more work though.

How to copy .java sources to Ant javac destFolder

I know how to use Ant to copy files and folders but what I'm interested in is if, and how, I can have the javac task copy the same sources it's compiling to the output directory.
Basically, it's very similar to the option to include your sources in the jar task.
Why not simply use the copy task, along with the javac one ?
You can even use ant's macro to define your own copyingjavac task, that performs the two operations, with the only problem to correctly handle filesets, to copy exactly the set of files being compiled.
If you want to only copy a file when compilation succeeded, you will have to either build a custom ant task (extending the default javac task), or to play with ant_contrib foreach task.
The macrodef could look like:
<macrodef name="copyingjavac">
<attribute name="srcdir"/>
<attribute name="destdir""/>
<element name="arginclude"/>
<sequential>
<javac srcdir="#{srcdir}" destdir="#{destdir}" updatedProperty="build.success">
<arginclude/>
</javac>
<copy todir="#{destdir}">
<fileset dir="#{srcdir}">
<arginclude/>
</fileset>
</copy>
<fail unless="build.success">
Build failed. Check the output...
</fail>
</sequential>
</macrodef>
I found this answer on Ant's website (you can remove the "excludes" part to copy the .java sources along the compiled versions):
...
<target name="compile">
<mkdir dir="${classes.dir}"/>
<javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath"/>
<copy todir="${classes.dir}">
<fileset dir="${src.dir}" excludes="**/*.java"/>
</copy>
</target>
...
This copies all resources (as long as they haven't the suffix ".java") to the build directory, so we could start the application from that directory and these files will included into the jar.

Categories