ant-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Adam Murdoch" <>
Subject RE: [PATCH] myrmidon task configurer
Date Wed, 02 Jan 2002 00:53:36 GMT

> From: Peter Donald []
> > Logging would be the main one - say I want to do logging in some deeply
> > nested obect, I need to have something pass me a Logger.  Ideally, my
> > object would simply implement LogEnabled, and the container would supply
> > the object with a Logger as part of its "initialisation".  The 
> alternative
> > would be for the object's parent to supply the logger - this 
> would be bad,
> > as it would force the parent to be LogEnabled.
> ...
> > It's possible that a data type might need other components, or the task
> > context, to do its work.  Again, having the container supply 
> these would be
> > a good thing.
> I would like to move away from having sub-elements being 
> "intelligent" and 
> move towards them just being passive data containers. When you 
> need to get 
> generate complex information from these items (like processing a 
> patternset 
> to get a list of files) you pass these passive data items to a 
> active service 
> (ie FileScanner or something) that would do all the magic for you.

I'm not so sure about this one.  It's a good pattern, but its necessarily a good restriction
to force on all sub-elements.

We already deal with sub-elements that break this model - for example, anywhere where a task
can be a sub-element (in <project>, <target>, <parallel>, <try><catch><finally>,
whatever).  If we can generalise the solution a touch, then we can deal with arbitrary sub-elements
that are "active".  It's a trade off - if we can generalise cleanly, then Ant becomes far
more adaptable for it.   If we can't come up with a good generalisation, its not a big deal
- right now, we don't have any active sub-elements that aren't tasks (though, I think this
is more due to Ant 1 limitations, rather than the concept not being useful).  I feel that
it is something to at least try.

Here's why I feel splitting passive data container and active evaluator is a good pattern,
but not a good rule:

* It becomes awkward when there is a reasonable degree of polymorphism.  In general, you've
either got to have an evaluator which understands every possible data container (bad, you've
lost the main strength of poymorphism), or a data type implementation has to provide both
a container and an evaluator (bad, extra work for task writers).  In many cases, it's simpler
just to provide a data type implementation that encapsulates both config and behaviour.

However, there are other cases where the separation is powerful, where alternative evaluators
can be swapped in, and the data container reused.

* Sometimes the "data type" actually represents a service.  For example, a compiler or a VFS.
 The data container/evaluator split stops being useful.

* It forces the task writer to do the evaluation - that is, finding an appropriate evaluator,
and doing the evaluation.  Ok, not a lot of code in each instance, but it does add up.  Often
the task is only interested in the result of the evaluation, and does not care where the result
comes from.  In these cases, having the container supply the result to the task saves work
for the task writer.  And gives the container much more flexibility in how to go about doing
the evaluation.

On a completely different topic, I'm wondering whether introspection might be a better way
to deliver services to a task, rather than using Composable and Contexualizable.  By "services"
I mean both core runtime services and task-specific services: 
  - Loggers.
  - Task executor.
  - An execution manager (for external commands).
  - file system manager (resolving file names, creating file systems, etc).
  - Compiler adaptors.
  - etc.

It's nicely declarative, and could take advantage of things like the TaskInfo work, and the
configuration stuff above.  It would also be an easy win for allowing user and/or build file
control over these things.

It might work something like this:  For each service required by a task, a service instance
can be specified in the build file, either inline or by reference.  If an instance is not
specified, a default instance is provided by the container.  The container decides on which
instance to use:
  - The user may provide an instance in their preferences.
  - A tasklib may provide a default instance for services it introduces.
  - The container may provide default instances for core things like loggers, etc.
  - There may be no default at all.

An example:

public class Javac extends AbstractTask {
    public void setCompiler(JavaCompiler compiler) { ... }

would allow build files like:

    <compiler type="jikes" pedantic="true" failOnWarning="true"/>

Or, by reference:

   <jikes id="java-compiler" pedantic="true" failOnWarning="true"/>

   <target name="compile">
       <javac compiler-ref="java-compiler"/>

   <target name="another-compile">
       <javac compiler-ref="java-compiler"/>

Or, say, the following in my ~/.antrc:

    <jikes pedantic="false" emacsOutput="true"/>


To unsubscribe, e-mail:   <>
For additional commands, e-mail: <>

View raw message