Where is <first> property/task described? - ant

By trial and error I got ANT example below working.
Looks like <first> is something executed before any target invoked.
E.g. ${foundSrcFiles} can be used in (any) target with found files
being displayed.
It's just I didn't find this documented anywhere. Anyone a link to description???
<project basedir="./example4" default="fourthTarget">
<property name="dir.src">src</property>
<first id="foundAnyJavaFile">
<!-- but things you can do here are limited. -->
<!-- property name="dir.classes"> doesn't e.g. work... -->
<fileset dir="${dir.src}" includes="**/*.java"/>
</first>
<property name="foundSrcFiles" refid="foundAnyJavaFile"/>
...
</project>

first is a type of resource collection in Ant. It takes another nested resource collection and selects the first X resources it contains. When X is not specified with the count attribute, it simply takes the single first resource.
It has nothing to do with the order in which things are executed in your build script. Any tasks that exist outside of targets (i.e. at the root project level) will be executed sequentially before any targets are invoked. In the example you posted, the property "dir.src" will be defined first, then the first resource collection will be defined with id "foundAnyJavaFile", and lastly the property "foundSrcFiles" will be defined with the reference "foundAnyJavaFile".
This resource collection is documented here: https://ant.apache.org/manual/Types/resources.html#first

Related

What are the usage restrictions on Ant's if/unless conditional attributes?

So we're using basic Ant 1.9.4, with a little use of 1.9.1's new if/unless attributes. For example,
<project xmlns:if="ant:if" xmlns:unless="ant:unless">
.... miles and miles of XML ....
<jar ....>
<service ....>
<provider classname="a.b.c.X" if:true="${some.flag}"/>
<provider ..../>
</service>
<fileset>
....the stuff in here will matter shortly....
</fileset>
</jar>
with some.flag always set to true or false, never left unset, and never set to any other value (assigned inside a property file read in earlier, if that matters), and it works wonderfully, giving us exactly the behavior we need. Joy!
More recently, we tried to make some of the jar task's fileset entries a little smarter, such as
<fileset dir="somedir">
<include name="optional_file" if:true="${some.flag}"/> <!-- same property name as before -->
</fileset>
With this syntax, we get an error "A zip file cannot include itself", with a line number pointing to the start of the jar task. This is obviously bogus syntax. However, changing this second if:true to simply if out of desperation -- and making no other changes -- avoids the error and gives us correct flag-based optional inclusion behavior.
What's going on here? Is the new syntax simply unavailable in <fileset> and/or fileset's nested <include> blocks?
As an experiment, I tried using an if:true or if:set attribute as appropriate in a few other useful places. Some places it worked perfectly. Some places I got some bizarre nonsense error, clearly the kind of thing that a parser prints when it's gone off the rails. Each time, reworking if:set="$(foo}" into if="foo" and if:true="${foo}" into if="${foo}" got back to the desired if-then behavior. So it's not a blocking problem, but we'd rather have the self-documenting :condition if we could.
I couldn't find mention of any such restriction in the Ant manual, but the manual describes the if/unless syntax in at least two different places using different descriptions. (I'm not sure where they are due to the manual's use of HTML frames; every URL shows up as index.html. Anytime I refer to the manual it feels like I'm browsing like it's 1999, baby! *does MC Hammer slide out of the room*)
Since Ant 1.4, <include> and <exclude> elements nested under <patternset> elements have supported if and else attributes. Each <fileset> has an implicit <patternset> nested under it, so the if and else attributes are available to it...
<condition property="some.flag.istrue">
<istrue value="${some.flag}"/>
</condition>
<fileset dir="somedir">
<!-- The "if" below is different than "if:true". -->
<include name="optional_file" if="some.flag.istrue"/>
</fileset>
In the above example, the if in <include> is an ordinary Ant attribute in the "default" Ant XML namespace. On the other hand, if:true is in the ant:if XML namespace.
The namespaced if:true doesn't work well with <include> elements. If the value provided to if:true doesn't evaluate to true, then Ant behaves as if the entire <include> element never existed. This is bad because Ant takes empty patternsets to mean "match every file". This is likely why you received the "A zip file cannot include itself" error; the <fileset> was likely containing the destination JAR file.
Stick with the plain if and else attributes for <include> and <exclude> elements and things should work.

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.

Create a fileset from a comma-separated list in a property without losing order

I use a specified property to create fileset:
<property name="cases" value="B.java,A.java,C.java" />
<fileset id="casesToBeRunning" dir="${src}" includes="${cases}" />
When casesToBeRunning created, I list the content of it:
<echo>Cases to be running: ${toString:casesToBeRunning}</echo>
it shows A.java,B.java,C.java which I'm not expected to.
I don't want Ant autosort for me, I need the original sort order of the property I defined to execute the cases orderly.
Anyone know how to handle this?
Ant filesets don't retain order - as you've seen. The related filelist type does respect ordering, so you might use:
<filelist id="casesToBeRunning" dir="${src}" files="${cases}" />
Whether the order is respected will depend on what task you plan to use to process the files. Most core Ant tasks that accept a fileset will accept a filelist instead, so you should be ok with them. For non-core tasks it may not work.
(Note that before Ant 1.8.0 some tasks didn't respect the order when traversing a filelist - among them copy for example).

concat as resource collection in zip not working?

I'm trying to use <concat> as a resource collection in a <zip> task and, according to the documentation, this should work. I'd like to do this because some of the files I want to include in the zip need to have some properties expanded, so I will also add a <filterchain> to the <concat> to do this. I'd prefer to do it directly rather than copying to a temp location (with property substitution) and including the copy in the zip file.
However, I can't seem to get <zip> to correctly use the <concat> element.
A simplified example of what I have so far:
<zip destfile="target/dist.zip">
<concat>
<fileset file="CHANGES.txt" />
</concat>
</zip>
This creates a zip file containing several directories all named concat (C: (obviously this is on a Windows machine).
What am I missing?
A colleague and I came up with the answer by looking through the <zip> and <concat> source. There are really two answers:
<concat>'s implementation of the ResourceCollection interface is odd, but we understand why.
There's a way around that.
For #1, while <concat> is a ResourceCollection (like FileSet), under the hood it returns as the name of single Resource it contains a hard-coded value:
"concat (" + String.valueOf(c) + ")";
Look familiar?
The name of resources is normally ignored--except by <zip> and its related tasks, which uses the resource name as the ZipEntry name. Since <concat> returns the odd-looking name, that's what we get in the zip file.
I haven't quite figured out why I get multiple entries, but it doesn't matter: the observation leads to a convoluted solution.
Since I know the name of the ZipEntry I want to create, I can use a <mapper> to give the <concat> resource a name. Here's what I came up with in all its glory:
<zip destfile="target/distribution.zip">
<fileset dir=".">
<exclude name="target/**" />
<exclude name="CHANGES.txt" />
</fileset>
<mappedresources>
<concat>
<fileset file="CHANGES.txt" />
<filterchain>
<expandproperties />
</filterchain>
</concat>
<mergemapper to="CHANGES.txt" />
</mappedresources>
</zip>
As my colleague says "In Ant every problem can be solved using a mapper."
This only works for Ant 1.8+ because <mappedresources> was added in that release.
I'm going to post some comments to the Ant mailing list and suggest a couple of enhancements:
Allow a resource name to be specified as an attribute on <concat> when it's being used as a ResourceCollection.
Throw an exception (and don't create a synthetic value) if getName() is called without having a value specified.
Finally, though not directly related, I do wish <expandproperties> could take a <propertyset> so I can control which properties get substituted.
Do you want the final zip to contain a single file or multiple files? As far as I can see, using concat (when done successfully, which isn't done above) would produce a single file, the result of concatenation of all files in the resource collection.
If you want multiple files rather than concatenation, I think intermediate copy is what you'll need.
From Ant manual for the concat task:
Since Apache Ant 1.7.1, this task can
be used as a Resource Collection that
will return exactly one resource.

Ant var and property scope

I have a main build script that calls various targets. One of these targets needs to store a value and another target needs to display it. Obviously this is not working so I think it may be related to scope. I've tried var, property, and declaring the property outside of target1. Since var seems to be mutable, it looks like I need to use it instead, but each time my output is empty.
Main script
<antcall target="target1"/>
<antcall target="display"/>
In target1:
<var name="myVar" value="${anotherVar}"/>
In display:
<echo>${myVar}</echo>
Do you really need to use <antcall>? Can you use target dependencies instead?
As you suspect, using <antcall> essentially creates a new scope.
antcall will start the ant target in a new project and will not affect the main project in any way. Try runtarget from antcontrib to run the targets in the same project.
You can call multiple targets with one antcall element. These targets will then share a single project instance including the properties defined. To do this specify the targets as nested elements like this:
<antcall>
<target name="target1"/>
<target name="display"/>
</antcall>
Another option I found was the antcallback, and it appears to work. This limits what is returned to just a particular list of values, which seems inherently safer than opening up the scope of the whole target (as it sets, creates, modifies many var and properties).
<antcallback target="target1" return="myVar"/>
<antcall target="display"/>
I think all of these are valid solutions, it just depends on what level you want to change the variable scope at.
<antcall target="display">
<param name="param1" value="anything" />
</antcall>
put the above code in your target1. I am sure you will be able to access your param1 in display now.

Resources