I expected that if the script in the <groovy> tags returned false or non-zero then the Ant task would fail but it is not the case. Got old Groovy if that makes a difference (1.8.6).
Throw an exception will cause ANT to fail. A cleaner way to do it would be to set a property within the script as follows:
<target name="build">
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy"/>
<groovy>
properties["groovy.error"] = true
</groovy>
<fail message="Fail baby fail" if="groovy.error"/>
</target>
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
I have an ant script that imports other scripts which import additional scripts leading to web of places where definitions can happen.
Can ant pull in all the definitions and output a file much like maven's help:effective-pom?
I'd like to add this as an output file with my build to make debugging somewhat easier.
You can create another target which will create that kind of file. For example it can look like this:
main build.xml which do some work and also can generate effective build:
<project name="testxml" default="build">
<import file="build1.xml" />
<import file="build2.xml" />
<target name="build">
<echo>build project</echo>
</target>
<target name="effective.build">
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy" classpath="C:\Program Files (x86)\groovy-2.1.4\embeddable\groovy-all-2.1.4.jar" />
<groovy>
def regex = ~/import file="(.*)"/
def buildXmlContent = new File('build.xml').text
new File('build.xml').eachLine {
line -> regex.matcher(line).find() {
def file = new File(it[1])
if (file.exists()) {
def replacement = file.getText().replaceAll(/<project.*/, '').replaceAll(/<\/project.*/, '')
buildXmlContent = buildXmlContent.replaceFirst(/<import.*/, replacement)
}
}
}
new File('build.effective.xml') << buildXmlContent
</groovy>
</target>
</project>
Two build.xml's created for tests:
<project name="testxml2" default="build">
<target name="clean">
<echo>clean project</echo>
</target>
</project>
<project name="testxml1" default="build">
<target name="test">
<echo>test project</echo>
</target>
</project>
If you will run ant effective.build you will get new build.effective.xml file with this content:
<project name="testxml" default="build">
<target name="test">
<echo>test project</echo>
</target>
<target name="clean">
<echo>clean project</echo>
</target>
<target name="build">
<echo>build project</echo>
</target>
<target name="effective.build">
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy" classpath="C:\Program Files (x86)\groovy-2.1.4\embeddable\groovy-all-2.1.4.jar" />
<groovy>
import java.util.regex.Pattern
def regex = ~/import file="(.*)"/
def buildXmlContent = new File('build.xml').text
new File('build.xml').eachLine {
line -> regex.matcher(line).find() {
def file = new File(it[1])
if (file.exists()) {
def replacement = file.getText().replaceAll(/<project.*/, '').replaceAll(/<\/project.*/, '')
buildXmlContent = buildXmlContent.replaceFirst(/<import.*/, replacement)
}
}
}
new File('build.effective.xml') << buildXmlContent
</groovy>
</target>
</project>
I chose groovy (because I learn it actually) for this purpose but you can create it in another langage. You can also compile this groovy code to ant task and use it as new task, for example <effectiveant outfile="build.effective.xml">. My example isn't perfect but it shows one of the solutions.
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
I'm using a groovy code snippet in an ant build file. Inside the groovy code I'm trying to reference a fileset that has been defined outside of the groovy part, like this:
<target name="listSourceFiles" >
<fileset id="myfileset" dir="${my.dir}">
<patternset refid="mypatterns"/>
</fileset>
<groovy>
def ant = new AntBuilder()
scanner = ant.fileScanner {
fileset(refid:"myfileset")
}
...
</groovy>
</target>
When I execute this I get the following error message:
Buildfile: build.xml
listSourceFiles:
[groovy]
BUILD FAILED
d:\workspace\Project\ant\build.xml:13:
Reference myfileset not found.
What am I missing?
According to the Groovy Ant Task documentation, one of the bindings for the groovy task is the current AntBuilder, ant.
So modifying your script to drop the clashing 'ant' def I got it to run with no errors:
<project name="groovy-build" default="listSourceFiles">
<taskdef name="groovy"
classname="org.codehaus.groovy.ant.Groovy"/>
<patternset id="mypatterns">
<include name="../*.groovy"/>
</patternset>
<target name="listSourceFiles" >
<fileset id="myfileset" dir="${my.dir}">
<patternset refid="mypatterns"/>
</fileset>
<groovy>
scanner = ant.fileScanner {
fileset(refid:"myfileset")
}
</groovy>
</target>
</project>