ant-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Jesse Glick <jesse.gl...@sun.com>
Subject Re: suggestion : Ant 1.8 full dist to include a 'scripting lang'
Date Fri, 15 Sep 2006 20:58:09 GMT
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.

-- 
jesse.glick@sun.com  x22801  netbeans.org  ant.apache.org
       http://google.com/search?q=e%5E%28pi*i%29%2B1


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


Mime
View raw message