ant custom-task, where should i place the new class file? - ant

writing your own task should be a simple task.
according to the documentation all you need is to extend org.apache.tools.ant.Task.
the site provide a simple class example:
package com.mydomain;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
public class MyVeryOwnTask extends Task {
private String msg;
// The method executing the task
public void execute() throws BuildException {
System.out.println(msg);
}
// The setter for the "message" attribute
public void setMessage(String msg) {
this.msg = msg;
}
}
and in order to use it using the build.xml:
<?xml version="1.0"?>
<project name="OwnTaskExample" default="main" basedir=".">
<taskdef name="mytask" classname="com.mydomain.MyVeryOwnTask"/>
<target name="main">
<mytask message="Hello World! MyVeryOwnTask works!"/>
</target>
</project>
my question is, where should i put the MyVeryOwnTask.java file, should it be .jar file?
should it be relative in some way to the build.xml file?
com.mydomain.MyVeryOwnTask does it represents a file structure like a java project in eclipse?
my ant directory is C:\ant.
i have all the environment vars set.
thanks.

The best approach is to put it in a jar file and then add a <classpath> inside the <taskdef> pointing to the jar:
<taskdef name="mytask" classname="com.mydomain.MyVeryOwnTask">
<classpath>
<fileset file="mytask.jar"/>
</classpath>
</taskdef>
You can put the jar in ant's own lib directory instead, and then you don't need the classpath, but using an explicit classpath element as above means your build file will work with a standard unmodified ant installation so it's a good habit to get into particularly if you are going to be collaborating with other developers.

Related

Worklight WAR ant configuration to include the Java files

I'm using WL 6.2.0.1 and one of our projects contains a Java files inside the WL server directory.
When I do the build for war file through eclipse I follow the following steps:
1- Right click on the project name > Build Project.
2- Right click on the project name > IBM Mobile Application Platform Pattern > Build Worklight EAR file.
Which generates to me the war file and inside it I see the java files.
When I switched to use ANT script to build the WAR file, I can't see the Java files anymore inside the war file:
ANT script:
<?xml version="1.0" encoding="UTF-8"?>
<project name="MobileApp" default="package" basedir="../">
<property name="WL_PATH" value="./buildscripts"/>
<property name="project.name" value="MobileApp"/>
<taskdef resource="com/worklight/ant/defaults.properties">
<classpath>
<pathelement location="${WL_PATH}/worklight-ant6.2/worklight-ant-builder.jar"/>
</classpath>
</taskdef>
<target name="WAR_CREATE">
<war-builder projectfolder="${basedir}/temp/source/${project.name}"
destinationfolder="bin/war"
warfile="bin/MobileApp.war"
classesFolder="classes-folder"/>
</target>
</project>
I'm not sure if I need to add the Java element to the script so it will compile the Java files. but I tried to add <Javac> but didn't work.
I referred to the following URL : https://ant.apache.org/manual/Tasks/javac.html. But Didn't know which one to use.
Any help thanks.
XML
<?xml version="1.0" encoding="UTF-8"?>
<project name="myProject" default="all">
<taskdef resource="com/worklight/ant/defaults.properties">
<classpath>
<pathelement location="cli_install_dir/public/worklight-ant-builder.jar"/>
</classpath>
</taskdef>
<path id="server-classpath">
<fileset dir="..\jars\Resources" includes="worklight-jee-library.jar" />
<fileset dir="..\jars\Resources\dev" includes="**/*.jar" />
</path>
<mkdir dir="bin\classes"/>
<javac
srcdir="${worklight.repositary}\${proj.brcname}\server\java"
classpathref="server-classpath"
destdir="bin\classes"
verbose="true"
includeantruntime="false"
target="1.6"
/>
<target name="all">
<war-builder projectfolder="."
destinationfolder="bin/war"
warfile="bin/project.war"
classesFolder="bin\classes"/>
</target>
</project>
The Above is the XML is used to Create a War file along with java classes.
Note :
In the places of dir , location and srcdir replace the content directory with your Locations.
Unlike when using the Studio which automatically compiles any Java files that reside under the server\ folder, this does not happen when using the Ant task scripts.
You must first compile these files and point to the folder containing the resulting .class files. This pointing is done in the classesFolder attribute in the Ant task script.
For further elaboration see this answer by me: https://stackoverflow.com/a/30302415/1530814

Invoke ant target from another target with <groovy>

I have the following script:
<target name="query">
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy" classpathref="libraries"/>
<groovy>
import groovy.sql.Sql
def sql = Sql.newInstance("jdbc:oracle:thin:#mydomain.com:1521:alias", "test", "test", "oracle.jdbc.pool.OracleDataSource")
List productNames = sql.rows("SELECT name from PRODUCT")
//println(productNames.count)
productNames.each {
println it["name"]
// HOW TO INVOKE ANT TARGET TASK HERE? TARGET TASK WILL USE it["name"] VALUE
}
properties."productNames" = productNames
</groovy>
</target>
<target name="result" depends="query">
<echo message="Row count: ${productNames}"/>
</target>
I would like to invoke another ant target from "query" target. Especially inside of productNames loop, like put in comments above.
Do you have any idea how to do it?
There are some binding objects in the <groovy> scope (see the documentation for more details), more specifically there is ant object which is an instance of AntBuilder (see the api here), with this object you can invoke getProject() method to get an instance of org.apache.tools.ant.Project and with this Project you can use executeTarget(java.lang.String targetName) method to execute a different target passing its name. All together looks like: ant.getProject().executeTarget("yourTargetName") and in your code:
<target name="query">
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy" classpathref="libraries"/>
<groovy>
import groovy.sql.Sql
def sql = Sql.newInstance("jdbc:oracle:thin:#mydomain.com:1521:alias", "test", "test", "oracle.jdbc.pool.OracleDataSource")
List productNames = sql.rows("SELECT name from PRODUCT")
//println(productNames.count)
productNames.each {
println it["name"]
ant.getProject().executeTarget("yourTargetName")
}
properties."productNames" = productNames
</groovy>
</target>
EDIT BASED ON COMMENT:
Passing parameters to the ant call it's not possible through org.apache.tools.ant.Project methods, however there is another way to do so, using ant or antcall tasks through AntBuilder, using antcall however it's not supported inside <groovy> if you try to use it you will get this error message:
antcall not supported within AntBuilder, consider using 'ant.project.executeTarget('targetName')' instead
So you must use ant task. For example if you have the follow ant target with a parameter in your build.xml:
<target name="targetTest">
<echo message="param1=${param1}"/>
</target>
You can call it from <groovy> passing a parameter like this:
<target name="targetSample">
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy" classpathref="groovyLibs"/>
<groovy>
ant.ant(antfile:'build.xml'){ // you antfile name
target(name:'targetTest') // your targetName
property(name:'param1',value:'theParamValue') // your params name and values
}
<groovy>
</target>
If you execute this <groovy> target example with ant targetSampe you will get:
targetTest:
[echo] param1=theParamValue
BUILD SUCCESSFUL
Total time: 0 seconds
Hope this helps,

How to set Ant reference to a FileSet from within <groovy/> Ant task?

I have something like:
<groovy>
import org.apache.tools.ant.types.FileSet
import org.apache.tools.ant.types.selectors.FilenameSelector
def aoeu = new FileSet()
aoeu.setDir(new File('aoeu'))
def snth = new new FilenameSelector()
snth.setName('snth')
aoeu.add(snth)
project.references['aoeu'] = aoeu
</groovy>
But:
<echo message="${toString:aoeu}"/>
emits a NullPointerException.
How can the above be fixed?
The groovy task has preconfigured bindings to an instance of AntBuilder and the Ant "project" object. It makes groovy very attractive for creating programmed logic within the build.
The following example demonstrates how the fileset can be created within the groovy script:
<project name="demo" default="process-text-files">
<path id="build.path">
<pathelement location="/path/to/jars/groovy-all-2.1.1.jar"/>
</path>
<target name="process-text-files">
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy" classpathref="build.path"/>
<groovy>
ant.fileset(id:"textfiles",dir:"src", includes:"**/*.txt")
project.references.textfiles.each {
println it
}
</groovy>
</target>
</project>
The following example demonstrates referencing a normal fileset created externally to the script (my preferred way to do it):
Ant : Process the files in the subfolder using tasks

How to rename files and folders with Ant

How to rename many files and folders with Ant? For files I know I can do it like this; question
How to do the same thing for folders?
For e.g.
Set of folders (Input)
com.google.appengine.eclipse.sdkbundle_1.5.2.r37v201107211953
com.google.gdt.eclipse.designer.doc.user_2.3.2.r37x201107161328
com.google.gdt.eclipse.designer.hosted.2_0.webkit_win32_2.3.2.r37x201107161253
org.eclipse.acceleo.common_3.1.0.v20110607-0602.jar
Output:
com.google.appengine.eclipse.sdkbundle_1.5.2
com.google.gdt.eclipse.designer.doc.user_2.3.2
com.google.gdt.eclipse.designer.hosted.2_0.webkit_win32_2.3.2
org.eclipse.acceleo.common_3.1.0.jar
For complex operations I use the groovy ANT task.
The following example will rename your files and directories, using regular expressions:
<project name="demo" default="rename">
<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.0.6/groovy-all-2.0.6.jar"/>
</target>
<target name="rename">
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy"/>
<fileset id="filesToBeRenamed" dir="build"/>
<dirset id="dirsToBeRenamed" dir="build"/>
<groovy>
project.references.filesToBeRenamed.each {
String origName = it
String newName = origName.replaceAll(/_[0-9\.]+[a-z0-9\-]+/, "")
if (origName != newName) {
ant.move(file:origName, tofile:newName, verbose:"true")
}
}
project.references.dirsToBeRenamed.each {
String origName = it
String newName = origName.replaceAll(/_[0-9\.]+[a-z0-9\-]+/, "")
if (origName != newName) {
ant.move(file:origName, tofile:newName, verbose:"true")
}
}
</groovy>
</target>
</project>
NOTES:
The "bootstrap" target only needs to be run once. It will download the groovy jar from Maven central
This will move the directory and all of its sub-directories and files.
<move todir="${toDir}">
<fileset dir="${fromDir}"/>
</move>
Take a look at the Ant-Contrib tasks. One is the <for> task. This will allow you to specify multiple directories, directory patterns, etc. This way, you can loop through the directories and files.
You can copy the files and directories to another location and use the Mapper to map the file names. (<move> task will also work with mappers.)
I recommend you download the Ant-Contrib Jar file to the directory ${basedir}/antlib/ac in your project. Then do this in the beginning of your build file:
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<fileset dir="${basedir}/antilb/ac"/>
</classdef>
<taskdef>
This will define the ant-contrib tasks and allow you to use them. If you're using a version control system and check everything in, someone can checkout your project and do a build without having to install Ant-Contrib first.

Is there a way to guarantee that an ant dependency is run only once?

My question is similar to avoiding-re-building-prerequisites-in-ant, except that my need doesn't involve created objects, but processes invoked, so the solutions discussed there won't work for me. At least I think so - but I'm new to ant.
My situation is that I'm writing a set of ant targets, and I need the lets-call-it setup target to be executed once and only once, no matter which target is invoked. Here's a greatly simplified example:
<?xml version="1.0"?>
<project name="Ant_Test" basedir=".">
<target name="setup">
<echo message="In setup" />
</target>
<target name="do.several" depends="setup">
<echo message="In do.several, calling do.first" />
<antcall target="do.first" />
<echo message="In do.several, calling do.second" />
<antcall target="do.second" />
</target>
<target name="do.first" depends="setup">
<echo message="In do.first" />
</target>
<target name="do.second" depends="setup">
<echo message="In do.second" />
</target>
</project>
I need setup to be invoked exactly once, regardless of whether do.several, do.first, or do.second are invoked. With my naive attempt above, invoking do.several results in three calls to setup.
I've thought of setting a property (let's call it setup.has.been.invoked), and using that to conditionally invoke setup from within each target, but it appears that property setting is limited to the scope it's done in, so if in setup, I set setup.has.been.invoked to true, that value only exists within setup.
What am I missing? Is there a section of the tutorials or online documentation I've skipped? Any pointers or hints?
You should remove the antcalls and add do.first and do.second as dependencies of do.several:
<target name="do.several" depends="setup, do.first, do.second">
</target>
This will make sure, that setup is only called once:
setup:
[echo] In setup
do.first:
[echo] In do.first
do.second:
[echo] In do.second
do.several:
BUILD SUCCESSFUL
Total time: 0 seconds
Documentation says why a property set in setup does not work with antcall:
The called target(s) are run in a new project; be aware that this means properties, references, etc. set by called targets will not persist back to the calling project.
I just would like to add another possible way to do this.
<target name="setup" unless="setup.already.executed">
<echo message="In setup" />
<property name="setup.already.executed" value="x" />
</target>
This way you run it only once and then instantly set the flag that it was already executed once. Also it doesn't break the "depends" part of your code, since it only runs targets if it is possible/necessary, but it doesn't break the execution of the target dependent target.
Also this is the least amount of change in your scripts.
Edit:
Explanation of the 'doesn't break the depends part' :
If 'ant do.first do.second' is invoked, it results in setup being called twice even with all targets using setup as a dependency. That would be a problem if setup is doing things like cloning a repo or other time consuming operations. This approach works for both cases - i.e. 'ant do.several' or 'ant do.first do.second' .
An alternative to the answers you've already received is to create a custom task container that makes sure it does not repeat an action. I have such a custom task in my personal Antlib, but there's a whole load of other junk in there that you probably don't want, so maybe you could just copy the source and add it to your own project. It looks something like this:
import org.apache.tools.ant.Task;
import org.apache.tools.ant.TaskContainer;
import org.apache.tools.ant.BuildException;
import java.util.List;
import java.util.LinkedList;
/**
* Task container to ensure that some set of actions is only performed
* once per build. On completion of the actions a property is set that
* prevents subsequent executions.
*/
public class Once extends Task implements TaskContainer
{
private final List<Task> tasks = new LinkedList<Task>();
private String property;
/**
* The name of the property to consult in order to determine whether
* the nested tasks should be performed. This is a required attribute.
*/
public void setProperty(String property)
{
this.property = property;
}
public void addTask(Task task)
{
tasks.add(task);
}
#Override
public void execute() throws BuildException
{
if (property == null || property.length() == 0)
{
throw new BuildException("Property name must be specified.");
}
// If no value is specified, the tasks are performed if the property
// is set to any value. If a value is specified, the tasks are only
// performed if the property matches that value.
if (getProject().getProperty(property) == null)
{
for (Task task : tasks)
{
task.perform();
}
getProject().setProperty(property, "done");
}
}
}
Targets can have an "unless" element that will skip the target if the property referenced by "unless" is set.
See the difference, between include and import in the Ant manual. Also use macrodefs.
I've slightly adapted your example, you'll need several files:
build.xml, common.xml and macrodef_project_setup.xml
build.xml
<?xml version="1.0"?>
<project name="Ant_Test" basedir="." default="init">
<import file="common.xml"/>
<!-- This target overridden by the one in common.xml -->
<target name="common.init"/>
<target name="setup" depends="init"/>
<target name="do.several" depends="common.setup">
<echo message="In do.several, calling do.first" />
<antcall target="do.first" />
<echo message="In do.several, calling do.second" />
<antcall target="do.second" />
</target>
<target name="do.first" depends="common.setup">
<echo message="In do.first" />
</target>
<target name="do.second" depends="common.setup">
<echo message="In do.second" />
</target>
</project>
common.xml
<?xml version="1.0"?>
<project name="common">
<target name="init">
<project_setup option="Stack.Over.Flow"/>
</target>
<target name="setup">
</target>
<import file="macrodef_project_setup.xml"/>
</project>
macrodef
<?xml version="1.0"?>
<project name="project_setup" basedir=".">
<macrodef name="project_setup">
<attribute name="option" default=""/>
<sequential>
<!-- some process -->
<echo>THIS IS MY SETUP OPTION: #{option}</echo>
</sequential>
</macrodef>
</project>
Output:
ant -p
Buildfile: build.xml
Main targets:
Other targets:
common.setup
do.first
do.second
do.several
init
setup
Default target: init
Default target is now init.
ant
Buildfile: build.xml
Ant_Test.init:
[echo] In setup initialization
[echo] THIS IS MY SETUP OPTION: Stack.Over.Flow
But you could still use ant setup.
ant setup
Buildfile: build.xml
Ant_Test.init:
[echo] In setup initialization
[echo] THIS IS MY SETUP OPTION: Stack.Over.Flow
Run it with do.several.
ant do.several
Buildfile: build.xml
Ant_Test.init:
[echo] In setup initialization
[echo] THIS IS MY SETUP OPTION: Stack.Over.Flow
Ant_Test.do.several:
[echo] In do.several, calling do.first
Ant_Test.do.first:
[echo] In do.first
[echo] In do.several, calling do.second
Ant_Test.do.second:
[echo] In do.second

Resources