I was wondering if anyone would bring it up...
+1 (is >1 allowed?) for including a scripting language implementation in
the standard Ant distribution, so that we can rely on it being there. In
fact I would suggest making Ant 2.0 assume a script as its input, and
have a compatibility mode for old XML scripts.
-1 (or <-1 if allowed) on writing our own DSL.
-0 on rewriting existing tasks in the Ant distribution in another
language. Would just confuse the code base. Focus on users of Ant, not
developers of Ant. Of course users should be able to write tasks in a
scripting language if they prefer. Ideally this would be the same thing
as just writing a plain old library file.
-0.5 on Lisp or Scheme. Don't get me wrong, I probably would have been
miserable as a teenager were it not for CLtLR2. But the last thing we
want to do is make life harder for a random Java developer to build his
or her program. Ant-in-Lisp would be far far better than the current XML
mess, to be sure, but I think a relatively mainstream language like
Rhino would be a lot more accessible. Lots of Java developers already
know JavaScript from web development anyway. BeanShell is also an easy
step for Java developers. JRuby and Jython are probably a bit more
accessible than Lisp but less than JavaScript and BeanShell.
My personal experience with Ant has always been that the tasks are
great, and the control flow is maddening. To write an interesting
script, I generally spend most of the time wrestling with if and unless
attributes, artificial targets set up to define properties at the right
times, weird <pathconvert> hacks, refid->string conversions, selectors,
and other gobbledygook. Generally all of this could be replaced with a
few lines of elementary JavaScript or any other general-purpose language
with sensible bindings and library functions.
The Ant scripts generated by NetBeans are also crippled by the lack of
scripting. For example, seemingly trivial problem: given a ${classpath}
which might be defined in some .properties files and which might include
files from anywhere on disk, copy the JARs into a build dest dir. Now
<copy> can take a fileset, but you have a path, not a fileset, and there
is no root. If the number of classpath elements were fixed, you could
write several <copy> tasks in sequence, but there can be any number of
elements. Solution? Either (1) regenerate a build script using XSLT from
some other definition file (eek!); (2) distribute a special task JAR to
copy a list of files specified by path, and make sure that this JAR gets
somehow referred to from every project's build script. An awful mess.
And in JS? All this could be replaced with something like
for (jar in classpath.split(':;')) {copy({file: jar, todir: destdir})}
which anyone can read and understand at once. Furthermore it is trivial
to see how to make a little modification to the above:
for (e in classpath.split(':;')) {
if (e.endsWith('.jar')) copy({file: e, todir: destdir})
}
If we had scripting bindings, we could get rid of or deprecate a lot of
weird task options, conditions, selectors, and so on. General rule of
thumb: if your XML grammar includes elements named <and>, <or>, and
<not>, you are doing something wrong. :-) Who needs to pore over the
reference documentation for a date selector when you can just write e.g.
newfiles = scan(basedir, '**/*.java', function(f) {
return f.lastModified() > ref.lastModified()
})
If targets were replaced by functions, and <import> by load(...), you
could compose scripts as normal library files, without having to sweat
over idiosyncratic inheritance and naming rules in the Ant manual, not
to mention the confusing ways that properties and references are passed
to subscripts, which does not match the semantics of any real language.
Things that look like function calls would really be function calls
(with parameters), lexically scoped and global identifiers would be what
they appear, variables could vary.
You could just say that by default all (top-level) functions in a script
are targets; if you wanted to use some library functions and expose only
certain targets, just use e.g.
function build() {
compile()
jar()
}
function compile() {
javac({srcdir: 'src', destdir: 'build/classes'})
}
function jar() {
jar({basedir: 'build/classes', jarfile: 'myapp.jar'})
}
function clean() {
delete({dir: 'build'})
}
TARGETS = [build, clean] /* build is first, so default */
If you really feel the need for infrastructure to run a given target
only once (which I do not think should be the default behavior), this
should be easy enough in JS:
function build() {
once(compile)
once(jar)
}
function compile() {...}
function jar() {
once(compile)
// ...
}
where once is defined to be something like this:
function once() {
var targ = shift(arguments)
if (!this.ran[targ]++) {
apply(targ, arguments)
}
}
Managing properties would also be far easier if you could be explicit
about their lifecycle and precedence. E.g. if run with ant -Dx=y and
build.properties containing "foo=value\nx=y2\nlocation.y=there":
print(x) // prints 'y'
var p = properties('build.properties') // load defs into map
print(p.foo) // prints 'value'
print(p.x) // prints 'y2'
add_defs(/* to */ this, /* from */ p, /* override? */ false)
print(foo) // now prints 'value'
print(x) // still prints 'y'
function sometarget(location) {...}
sometarget(this['location.' + x]) // called with 'there'
load('other.js') // defines function othertarget(defs) {...}
othertarget(p) // pass it what you loaded
othertarget(this) // pass it my own properties
I don't buy the argument that Ant is currently "declarative". It's
nothing of the sort in my experience. When you think about what a build
script does, it runs a sequence of actions, possibly with some control
flow, and uses variables which are defined through a potentially complex
series of interactions with the environment. That's a program, not a
declaration. Make tried to impose a Prolog-y model onto the problem,
with the result that only Prolog programmers could understand makefiles,
and recursive make never really followed the model anyway. Ant is easier
to understand because it works more like a sequential program, and file
up-to-date handling is bundled into tasks. So why is it written in a
cumbersome XML dialect which is incapable of expressing elementary
computations?
-J.
--
[EMAIL PROTECTED] x22801 netbeans.org ant.apache.org
http://google.com/search?q=e%5E%28pi*i%29%2B1
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]