How to write Custom Nant tasks of Nant tasks - task

Hi I new to nant and am looking to make some kind of function/task that groups a bunch of regular nant tasks.
Basically I have 3 websites with web.config files that I need to write the connectionstring and imageDir values 2, only the file path changes
Currently I am looping through the a csv of file paths to accomplish this but would like to be able to pull the code out of the loop and make it a task/function/target or something. What is the best approach.
<foreach item="String" in="${webConfig.includedFilePaths}" delim="," property="File.Path">
<do>
<xmlpoke
file="${File.Path}"
xpath="${connectionString.XPath}"
value="${connectionString.Value}" />
<xmlpoke
file="${File.Path}"
xpath="${imageDir.XPath}"
value="${imageDir.Value}" />
</do>
</foreach>
I am hoping for something like:
<writeWebConfig file=${file.Path}"

You can create a custom nant task with a property like:
<target name="UpdateConfig">
<property name="config.filepath" value="" />
<!-- Rest of the task code here -->
</target>
And then you can call this task as many times as you want with the custom value of the property config.filepath. You can override the property value from command line using -D like:
-D:config.filepath=D:\TestWeb\web.config

Related

How to pass nested arguments from one ant target to another

How to pass nested arguments from one ant target to another?
I need to pass a variable number of nested elements from one target to another.
I have a common file with all of my standard build tasks that's included in all of my projects.
I am adding a new custom task that takes a variable number of nested arguments
As a standard, all ant calls are made through the common file to ensure consistency of build style and logging.
Thus the new custom task and its nested child will be defined in the common script.
The project build script looks like this
<target name="projectBuild">
...
<ant target="_newFooTaskWrapper" antfile="commonFile">
<property name="_arg1" value="hello"/>
<property name="_arg2" value="world"/>
<nestedArg value="qux"/>
<nestedArg value="baaz"/>
...
<nestedArg value="AAAAA"/>
</ant>
...
</target>
The common script looks like this:
<target name ="_newFooTaskWrapper">
<echo message="Target _newFooTaskWrapper in project ${ant.project.name} from base directory ${basedir}"/>
<echo message="arg1 = ${_arg1}"/>
<echo message="arg2 = ${_arg2}"/>
<taskdef name="newFooTask" classname="org.foo.NewFooTask"/>
<typedef name="nestedArg" classname="org.foo.NewFooTask$NestedArg"/>
<newFooTask arg1="${_arg1}" arg2="${_arg2}">
<nestedArg value="qux"/>
<nestedArg value="baaz"/>
...
<nestedArg value="AAAAA"/>
</newFooTask>
Obviously, this isn't right. My question is, what's the right way to do this?
I need to pass a variable number of nested elements from one target to another.
For "varible", I assume you mean you don't know the exact number of the nested elements you want to pass to the task, so what you want is something like method(Object param...) in java, is it?
It's not a good idea to try such a way. Ant is not a scripting language but a build tool. It provides limited "scripting" possibilities.
However, you can try it in the following two ways:
1, If your nested element is just in the form of <elementName value="xx" />, you don't need anything complicated. Just pass another property containing a comma seperated list of the values, and process the list in your custom ant task. It's easy for Java to split the property into a list and process it.
2, If your nested element may be more complicated... maybe you can try reference:
Make a type fooTaskParams which can be referenced via an id:
<fooTaskParams id="_foo_task_params">
<nestedArg value="qux"/>
<nestedArg value="baaz"/>
...
<nestedArg value="AAAAA"/>
</fooTaskParams>
and pass the reference to the other build file:
<ant target="_newFooTaskWrapper" antfile="commonFile">
<property name="_arg1" value="hello"/>
<property name="_arg2" value="world"/>
<reference refid="_foo_task_params"/>
</ant>
and then make your task to be able to process the reference:
<newFooTask arg1="${_arg1}" arg2="${_arg2}" paramRefId="_foo_task_params" />
You may need to take care of reference override, or make your task able to process the ref as well as taking nested elements.
Read ant's manual about <ant> and <typedef> for more about this approach, and refer to SO Q&As like this when you encount any problem.

Condition Property Override

I want to use an condition property to set the property value to X if another property is defined and Y otherwise. However, I don't want the user to be able to override the condition property from the command line.
How can this be achieved?
Starting from ant 1.8 for some use cases local task may be applicable. Since a property is made local it starts with an empty value. It's scope is limited to current target, but you may pass it to subsequent targets using param argument in antcall.
Nope, you can't override a property set on the command line. At least, it's not easy to do. The whole purpose of overriding properties on the command line is to allow users to override defaults in order to make modification in the way your project builds. For example:
<property file="${basedir}/build.properties"/>
<property name="javac.debug" value="no"/>
<target name="compile">
<javac destdir="${main.destdir}"
debug="${javac.debug}">
By default, the Java code is compiled without debugging information. Maybe this is done to make jar files smaller, or faster interpretation, or maybe to make the code harder to decompile and read. Whatever reason, this build won't put debug information into the classfiles.
However, developers do want this debugging information, so they want to be able to override this setting:
$ ant -Djavac.debug=true compile
Or, they can create a build.properties file and put the value in there.
This type of issue comes up when you're not using Ant for builds. I know several sites that use Ant scripts to do deployments. I usually discourage this because Ant isn't really made for this type of thing. For example, Ant doesn't have any built in logic or loops. Once a property is set, it can't be changed. These are good ideas for a build language, but a terrible idea for a general purpose programming language.
Also, developers shouldn't be doing builds for QA or production. Those should be done by a build server that won't override defaults.
Now how to destroy this whole well thought out system and cause absolute havoc:
You can use the ant-contrib tasks in your project. Doing this will allow you to access the Ant Contrib var task to unset properties.
Download the ant-contrib.jar file (whatever the latest version is), and put it in a lib directory under your project. Then you can do this:
<project name="danger-will-robinson" default="package" basedir="."
xmlns:ac="http://ant-contrib.sourceforge.net">
<!-- Define the Ant-Contrib tasks -->
<taskdef=resource="net/sf/antcontrib/antlib.xml"
uri="http://ant-contrib.sourceforge.net">
<classpath>
<fileset dir="${basedir}/lib">
<include name="ant-contrib*.jar"/>
</fileset>
</classpath>
</taskdef>
<!-- Unset Property "foo", so you can use it -->
<ac:var name="foo" unset="true"/>
Note that the <classpath> points to the ant-contrib jar in the ${basedir}/lib directory. If you check that into your source repository, it will allow everyone who checks out your project to be able to do the build without installing the ant-contrib jar on their system.
Note that I've defined a "ac" XML namespace, so Ant-Contrib tasks won't overlap other possible third party tasks.
Properties in ant once set are immutable by design. You may overwrite an existing property with any scripting language that provides access to ant api, i.e. javascript.
JDK >= 1.6 already ships with a javascript engine, so you may use something like :
<project>
<property name="x" value="whatever"/>
<script language="javascript">
project.getProperty('x') ?
project.setProperty('foo', 'true') :
project.setProperty('foo', 'false');
</script>
<echo>$$[foo} => ${foo}</echo>
</project>
out of the box.But that won't help if someone uses ant -f yourbuild.xml -Dfoo=bla !! as userproperties (those properties defined via -Dkey=value) have a special protection.
So your requirement "..However, I don't want the user to be able to override the condition property from the command line". is not fullfilled.
But the let task from Ant addon Flaka provides the possibillity to overwrite even userproperties :
<project xmlns:fl="antlib:it.haefelinger.flaka">
<property name="x" value="whatever"/>
<!--
:= defines a new property whereas
::= overwrites any existing property
even userproperties
-->
<fl:let> foo ::= has.property['x'] ? 'true' : 'false'</fl:let>
<echo>$$[foo} => ${foo}</echo>
</project>
Run both scripts with ant -f yourbuild.xml -Dfoo=bla to see the difference.
Ant api has also method project.setUserProperty(String,String) so you may use also:
...
<script language="javascript">
project.getProperty('x') ?
project.setProperty('foo', 'true') :
project.setProperty('foo', 'false');
project.getUserProperty('x') ?
project.setUserProperty('foo', 'true') :
project.setUserProperty('foo', 'false');
</script>
...
to prevent the foo property to be set via .. -D .. and it will work even if property x is defined on commandline -Dx=whatever You have to make your choice, script task with javascript out of the box or Flaka let task
oneline solution but Flaka jar needed.

Passing optional path arguments to ant

I have an ant task that uses an apply task to run a script on a group of files.
I have a directory structure resultant of something like this:
mkdir -p a/{b,c,d,e}/f
Normally (if I pass no arguments), I would like ant to run on all fs.
That is, if I called ant mytask, it should process: a/b/f, a/c/f, a/d/f, a/e/f. This already works using apply and patternsets.
However, when I pass it an optional argument called foo, it should only call the script on a/foo/f.
So if I called ant mytask -foo b, it should process a/b/f only, and not the others.
I have read this SO post, which explains ways of passing arguments, and I have looked at the ant documentation regarding properties, and conditionals. But I am still unable to piece them together in a way that works.
Also, I do not want to use one of the suggestions from the SO above which called for arguments like this:
<arg value="${arg0}"/>
<arg value="${arg1}"/>
I want to be able to call it as ant mytask -foo valueoffoo for any arbitrary foo.
Thanks.
I tried martin clayton's suggestion below and have code like:
<target name="mytask">
<property name="foo" value="*" />
<apply executable="perl">
<arg value="somescript"/>
<dirset id="them" dir="a">
<include name="${foo}/*/f" />
</dirset>
</apply>
</target>
The above does what I want.
Note 1: In my actual code I use a patternset instead of dirset but it should work the same.
Note 2: In my original question I said the directory structure was a/{b,c,d,e}/f. It is in fact a bit more complicated, hence the * in the include above. I omitted that the first time around because it did not seem relevant.
You can do this - albeit with a slightly different command-line syntax -
using a property 'override'.
First, in the buildfile, construct your fileset or dirset from a property foo,
something like this:
<property name="foo" value="*" />
<dirset id="them" dir="a">
<include name="${foo}/f" />
</dirset>
This will give you your default behaviour - processing all
subdirectories of a that themselves have a subdirectory f.
Now, if you run Ant like this:
ant -Dfoo=d
Only directory a/d/f will be processed.
This works because Ant properties are not mutable - well, not normally anyway -
so the command-line definition of foo prevents the one within the buildfile from being used.

How do I make a fileset from a comma-separated list of directories in Ant?

In an Ant target I get a property, containing the list of directories to be included in further action (copying, filtering, etc.). It looks like this:
directories=dir1, dir2, dir3
I need a way to convert this list to a fileset or patternset that selects all the files in these directories.
I know I can use a script to generate pattern strings and then use it in the "include" or "exclude", but is there are a way to avoid scripts?
Note that as of Ant 1.9.4, there is a new construct <multirootfileset> that provides that functionality, even if the dirs are not siblings:
<multirootfileset basedirs="${directories}" includes="**/*">
How about using the antcontrib propertyregex task to convert the comma-separated list into wildcards suitable for a fileset?
<property name="directories" value="dir1, dir2, dir3" />
<property name="wildcard" value="${file.separator}**${file.separator}*" />
<propertyregex property="my_pattern"
input="${directories}"
regexp=", "
replace="${wildcard}," />
At this point we now have:
my_pattern=dir1/**/*,dir2/**/*,dir3
That can be used with a further suffixed wildcard to get the full fileset:
<fileset dir="." id="my_fileset" includes="${my_pattern}${wildcard}" />
(The fiddly ${wildcard} is to ensure portability between unix and windows filesystems, you could use /**/* if you're pure unix.)
Something like this should work:
<dirset includes="${directories}"/>
Yes, dirset isn't fileset. However, it may be enough, or else you can probably use a for or foreach from ant-contrib to iterate over the directories in your target. You might also be able to define a ResourceCollection based around the dirset. It might help to know what the "further action" is expected to be.
However, this feels like too much work ...

How can I iterate over properties from a file?

All my projects and their versions are defined in a properties file like this:
ProjectNameA=0.0.1
ProjectNameB=1.4.2
I'd like to iterate over all the projects, and use their names and versions in an Ant script.
At present I read the entire file using the property task, then iterate over a given list in a for loop like this:
<for list="ProjectNameA,ProjectNameB" param="project">
<sequential>
<echo message="#{project} has version ${#{project}}" />
</sequential>
</for>
How can I avoid the hard-coding of the project names in the for loop?
Basically iterate over each line and extract the name and the version of a project as I go.
Seeing as you're already using antcontrib for, how about making use of the propertyselector task:
<property file="properties.txt" prefix="projects."/>
<propertyselector property="projects" match="projects\.(.*)" select="\1"/>
<property file="properties.txt" />
<for list="${projects}" param="project">
...
</for>
The idea here is to read the properties once with the projects prefix, and use the resulting set of properties to build a comma-separated list of projects with the propertyselector task. Then the properties are re-read without the prefix, so that your for loop can proceed as before.
Something you want to keep in mind, if you are reading additional .property files (besides build.properties) is scoping. If you read an additional file (via the property file="foo.property") tag, ant will show that the file was read, and the properties loaded. However, when you goto reference them, they come up un-defined.

Resources