ant-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Richard Russell" <richard.russ...@db.com>
Subject Thinking in Ant...
Date Mon, 04 Oct 2004 14:18:06 GMT
Hi all,

Firstly, please excuse the crufty formatting of this email -- my employer 
makes me use Notes. If people would prefer, I can paste this into a public 
Twiki somewhere... Just let me know the general preference.

Secondly, I'm hoping that this email does not come across overly critical 
or negative -- I'm not having a go at ant, I honestly just want to get my 
head around these problems better.

Finally, thanks for your patience if you manage to read the whole email. I 
wanted to keep it short, but decided that the length was necessary to 
illustrate the problem that I've been having. Those interested, but 
pressed for time could just read the first part, up to the example...


OK, now to the actual content:

I'm having ongoing problems getting along with Ant. I keep wanting to do 
things that it appears that Ant can't do, despite trying to use what I 
would consider fairly basic constructs. I run into these issues so 
regularly that I cannot help but assume that I am simply not 'thinking in 
Ant', and am therefore fighting against its design rather than working 
with it. I occasionally come across snippets of documentation that seem to 
confirm this. Specific examples of things I keep butting my head against 
include:

properties being "immutable" (except in some cases -- see propertycopy)
not being able to double-dereference properties (eg ${${propertyname}}), 
or use arrays (not comma-separated lists -- indexable arrays)
lack of 'if', 'for' and similar logic constructs (yes, I am aware of 
ant-contrib, but much documentation seems to indicate that this is a 
concession rather than an ideal)
inflexibility of certain tasks/containers etc -- eg below, I want to set 
up a bunch of filters, and would like to use a construct like this within 
a loop:

                <filterchain id="${filtername}">
                        <replacetokens>
                                <for param="propertyname" 
list="parameterlist">
                                        <propertycopy name="propertyvalue" 
from="${prefix}.@{propertyname}"/>
                                        <token key="@{propertyname}" 
value="${propertyvalue}"/>
                                </for>
                        </replacetokens>
                </filterchain>

As you can see, this illustrates a couple of my problems -- I'm using 
propertycopy to get around the lack of double-dereferencing, and also 
taking advantage of the way propertycopy can mutate values of "immutable" 
properties. Then, I'm using a for loop (which requires mutable properties) 
to set up multiple tokens in the replacetokens filter (actually, I can't 
do this, as replacetokens doesn't support the nested 'for' element). 
Another example of this is the (to my mind) strange duplication between 
filterchains and filtersets. They seem to do basically the same thing, but 
in different places and with different limitations.

My frustration here is because Ant isn't procedural. Or perhaps it's 
because I think of my build process in a procedural manner (hence why I 
call it a build *process* and not a build *declaration*). Perhaps this is 
because I've always used make, but I'm not sure how else to think about 
it, at least at the low level. Eventually, I have battled through, and 
have created new tasks, macros, or used other people's libraries to make 
it all happen, but no matter what, it all takes so long, and I don't see 
life getting much better as I grow in familiarity with it.

------------------------------------------------

Here's a longer example of something that I would normally consider 
simple. I want to append each of the files in one directory to the 
same-named file in another directory, filtering some tokens as I go. It's 
possibly expressed simplest as a shell script:

----
for file in `ls ${dir1}/*.DAT`; do 
  cat ${dir1}/${file} | sed -e 's/@parameter@/value/' >> ${dir2}/${file}
done
----

As an aside, I wrote that particular line in about two minutes, including 
fixing the one error I made in the sed script (I missed the trailing /). 
Obviously, there would be more sed parts for the different tokens, and 
it's not exactly an elegant solution. But the point is that it took me two 
minutes, didn't require me consulting any API documentation, and doesn't 
require compilation. I'm sure there's more elegant, perhaps easier ways of 
doing it, in Perl, or even still in shell, but this is just a trivial 
example.

Now, to do the same in Ant, I first tried to combine the for and concat 
tasks, with a filter, in the obvious (to me) manner. For various reasons 
this wouldn't work -- mostly to do with immutable properties, and not 
being able to separate the filename from the directoryname in a fileset 
and so on. If anyone knows of a way to replicate this functionality 
without writing a new Task, let me know.  As it was, I had to create the 
following Task:

----
package mypackage;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.FileList;
import org.apache.tools.ant.taskdefs.Concat;

import java.io.File;


public class AppendCopy extends Concat
{
        private String destDir=null;
        private File srcFile=null;

        public void execute() throws BuildException
        {
                if (destDir==null)
                        throw new BuildException("AppendCopy: destdir 
attribute not set");
                if (srcFile==null)
                        throw new BuildException("AppendCopy: srcfile 
attribute not set");
                try
                {
                        File destinationFile=new 
File(destDir+System.getProperty("file.separator")+srcFile.getName());
                        super.setDestfile(destinationFile);
                        FileSet sourceFileSet=new FileSet();
                        sourceFileSet.setFile(srcFile);
                        super.addFileset(sourceFileSet);
                        super.setAppend(true);
                        super.execute();
                }
                catch (Exception exception)
                {
                        throw new BuildException("AppendCopy: something 
went wrong\n" + exception);
                }
        }

        public String toString() {
                return 
super.toString()+"\ntoDir="+destDir+"\nfromFile="+srcFile+"\n";
        }

        public void setDestdir(String string)
        {
                destDir=string;
        }

        public void setSrcfile(File file)
        {
                srcFile=file;
        }

        public void setDestfile(File destinationFile) throws 
BuildException {
                throw new BuildException("AppendCopy: destFile attribute 
disallowed");
        }

        public void setAppend(boolean append) throws BuildException {
                throw new BuildException("AppendCopy: Append attribute 
disallowed");
        }

        public void addFileset(FileSet set) throws BuildException {
                throw new BuildException("AppendCopy: Nested FileSet 
element disallowed");
        }

        public void addFilelist(FileList list) throws BuildException {
                throw new BuildException("AppendCopy: Nested FileList 
element disallowed");
        }
}
----

And then, I use it in my build.xml like this:

----
<taskdef name="appendcopy" classname="mypackage.AppendCopy"/>
...
<for param="filename">
        <fileset dir="${dir1}/" includes="*.DAT"/>
        <sequential>
                <appendcopy srcfile="@{filename}" destdir="${dir2}" 
fixlastline="true">
                        <filterchain refid="myfilter"/>
                </appendcopy>
        </sequential>
</for>
----

Plus, of course, the setting up of the filterchain "myfilter".

Now, when I look at the result, it's far from elegant, and without knowing 
what appendcopy really did, I would be hard pressed to understand what the 
ant snippet did. I could remove the for loop, and create a task that did 
the whole operation, and was called like this:

----
<appendcopydirectory todir="${dir2}">
        <fileset dir="${dir1}/" includes="*.DAT"/>
        <filterchain refid="myfilter"/>
</appendcopydirectory>
----

But I suspect that would be even more work. It's also (to my mind) less 
clear and less re-usable (though I could name it 
appendcopyeachfileinfilesettosamenamedfileintodir, but that's got its own 
problems). I could subclass copy, adding an "append" attribute, but, 
having read the source to Copy 
(http://cvs.apache.org/viewcvs.cgi/ant/src/main/org/apache/tools/ant/taskdefs/Copy.java?rev=1.66.2.6&view=markup),

I decided that discretion was the better part of valour, and there had to 
be an easier way. Note that while I can code Java, I'm not a developer, 
and have no desire to become one. Certainly, I challenge even a top 
developer to come up with some sort of append attribute for Copy in the 
time it took me to write the equivalent shell script above.

-------------------------------------------

So, what is it that I'm doing wrong here? Why am I always battling against 
Ant, and finding that the things I wish to do, things which I find simple 
to express in english, in psuedocode, or in shell, are so darned complex 
to do in Ant? Am I just not getting it? Am I driving in nails with a 
screwdriver? 


Cheers,

Richard Russell 
Deutsche Bank AG London 
Global Markets Customer Solutions
Office: +44 (0)20 7545 8060
Mobile: +44 (0)79 0661 2237

---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@ant.apache.org
For additional commands, e-mail: user-help@ant.apache.org


Mime
View raw message