Mutant Proposal
|
Goals
|
This page describes the key goals that have shaped the development of
Mutant.
The first section identifies a set of issues with Ant1 that have cropped up as
Ant1 has evolved. The design implications of each issue are then summarized as a
Mutant requirement. I do not want to suggest that these problems are unsolvable
within the Ant1 design. Already I believe we have seen the Ant2 proposals
influencing people trying to extend Ant1. These issues may be solvable, although at
the risk of backward compatability impacts or further complication of the Ant1
codebase.
The second section covers a set of additional requirements that have emerged as
the whole concept of Ant2 has developed. Many of these came from the discussions
on the Ant-Dev mailing list.
The realisation of these requirements as they impact a user is on the
next page. Th implications for task developers and
Ant developers are discussed in the following sections.
|
Ant1 Issues
|
Unrestricted core access
|
The interface between the Ant core and tasks is not controlled. It
allows tasks almost complete access to the internal data structures of the
core. This is poor encapsulation. Whilst most tasks do not need and do not
use this access, its existence nonetheless prevents changes being made to
the core without raising concerns about impacting backward compatability.
The uncontrolled nature of the task-core interface also makes it difficult
to reuse tasks and types in a different context without almost completely
duplicating the Ant core
|
Lack of embedding support
|
Ant1 does not provide strong support for embedding Ant in other systems,
particularly a GUI or IDE. The development of Antidote highlighted this
difficultly. Antidote was forced to perform its own XML parsing so it could
build its own model of the build. There is no communication medium for a GUI to
communicate with the core. Without this capability any GUI will be limited to
simply editing the XML representation of the build.
|
Configuration done at Parse-Time
|
The original implementation of Ant1 performed all task configuration at the
time the XML description of the build was parsed (parse-time). This approach
could not handle dynamically created tasks very well since in some
instances the type of the task to be configured was not known at parse-time.
This limitation was overcome with the introduction of the UnknownElement and
RuntimeConfigurable classes. While these work well, they are confusing and at
times surprising to most Ant developers. Also some operations still occur at
parse-time, notably creation of nested elements of known types. This could be
overcome by going to a fully dynamic model but the fear of backward
incompatability constrains this change from occuring.
|
Multiple execution
|
In Ant1, a task, once configured, may be used more than once - i.e. its
execute method may be called more than once. This occurs when the Ant
command line specifies the evaluation of two targets. If those targets have
overlapping dependencies, the tasks in those overlapping dependencies will
be executed twice. This arrangement requires task writers to preserve the
state configured by Ant during the execution of the task so that the next
execution has the correct configuration.
Many task writers are not aware of this requirement. Frequently the task's
state is changed during the execution method, which can lead to mysterious
falures during subsequent executions. This need to preserve the configuration
state makes writing tasks much harder.
|
No automatic deployment of tasks
|
Ant1 has a set of well-known tasks (and types). A well-known task is one
for which a mapping between the taskname and its implementing class is
predefined in Ant. This mapping is provided by the two defaults.properties
files in Ant's code. These well-known tasks do not need to be taskdef'd to
make them available in a build file. Conversely tasks which are not in this
list need to be explicitly taskdef'd.
Note that this distinction is not the same as the core/optional
distinction found in Ant1. An Ant1 core task is notionally supported by Ant
without requiring any additional external libraries - it just requires the
classes available in the 1.1 JDK, in Ant and in the libraries Ant uses such
as the XML parser. An optional task is a task which requires JDK 1.2+
features or the support of an external library, such as jakarta-regexp.
The problem with this system is that there is no namespace management. If a
new task is added to Ant, it may invalidate buildfiles which are already
using that taskname via a taskdef. Actually the taskdef may continue to work or
it may not - it will depend on the nested elements that the task definitions
support (see above discussion of regarding cofiguration at parse-time)
|
Top level tasks
|
In Ant1 certain tasks may appear outside of any target. This is implemented as a
set of hardcoded conditions in the XML parsing phase. The execution and
configuration of these tasks does not involve the use of RuntimeConfigurable and
UnknownElement.
This hardcoding is not very inituitive. It is confusing to users who do not
know why only some tasks may be run outside of a target. Also as the list of
tasks that may be useful as a top-level task grows, further special cases must
be added to the code making the code harder to maintain.
|
Build file inclusion is cumbersome
|
In Ant1, the inclusion of a set of build file definitions or targets is achieved
through the use of XML entities. This is just cumbersome.
|
Classpath management
|
Probably the greatest issue facing Ant1 today involves the management of the
classpath and the associated visibility of classes. Most optional tasks in Ant1
require the supporting classes for the task to be available on the system
classpath. For example, the JUnit task is only usable if the JUnit jar is on
the classpath. If it is not, Ant will complain about being unable to create the
junit task.
The usual solution when a user runs into this problem is to put the required jar
into the ANT_HOME/lib directory. This just adds the required jar to the system
classpath prior to starting Ant albeit without requiring the user to explicitly
set the classpath.
There are a number of issues with this approach. The classes on the
classpath share the same classloader as Ant's classes and Ant's support
libraries - particularly the XML parser. If a task wishes to use a specific XML
parser, it may conflict with Ant's own parser. If two tasks require different
versions of a supporting jar, these will conflict.
Many tasks make use of factory objects (dynamic class instantiation, dynamic
resource loading, etc). When the factory is in the system classpath it will not
be able to load resources available in a taskdef'd task's custom classpath due
to the delegating nature of classloaders. Such errors can be very confusing to
users. More recent code is likely to attempt use of a context classloader but
this is not set by Ant1 currently.
|
Extensibility
|
The earliest versions Ant1 did not explicitly support any datatypes. The only
datatype, property, was actually created as a side-effect of running the
<property> task. If it were to be implemented today, property
would probably be known as a string datatype, perhaps associated with a
<load-properties> task. This is also the reason property
is one of those tasks which is allowed to exist outside of a target.
As Ant1 evolved new types such as <path> and
<fileset> were added. The concept of datatypes has continued
to evolve to the point where users can now define new datatypes. It would be
expected that in addition to new types there will be many sub-types created,
such as new types of fileset. Ant1, however, does not strongly support such type
extensibility. When a subtype is created by extending an existing type, Ant1
requires it to be either used by reference or all tasks which accept the base
type need to be modified to support the new type. Use by reference is not always
possible. It will depend on whether the base type has been coded to support
references.
|
Project object does too much
|
In Ant1 the Project object takes on too many roles including all of the
following:
- Holds the project model built when parsing the build file
- Holds the run-time state of the build through the properties and current
task definitions
- Provides context for task execution. All tasks have a reference to their
project instance and use it to access core functions
- Provides the implementation of common task operations
- Build event management
- Message logging
- Global filter definitions
- Java Version
- Property replacement
- Message Levels
- Utility functions such as boolean conversion and path translation
- Integration point for embedding Ant
As a class, Project is not cohesive. Reuse of the Ant core functionality is
difficulty as all of these concerns bring in many other support classes.
|
|
Other Goals
|
In addition to the issues which arise from Ant1 limitations, there are a number
of requirements which have emerged in the mailing list discussions about Ant2.
This isn't a complete list - just the ones I picked up for Mutant.
Limited project reuse
|
In Ant1 the only way to reuse build file definitions is to use the
<ant> task. While useful, it is relatively coarse-grained. It
can also be tedious if you are trying to extend an existing project definition -
that is, adding new targets while retaining a user's ability to access the
existing targets.
In addition to the extension of projects, it should be possible to compose
projects creating dependencies between the targets of the different projects,
and to access the data and definitions of one project by a controlling project
|
Ant1 Compatibility - Zero Friction
|
While it has always been accepted that there will be come backward compatibility
breaks in moving from Ant1 to Ant2, these should be minimized. If the changeover
from Ant1 to Ant2 is difficult, it may never happen. On the other hand, the
openness of the Ant1 interface means that some tasks will not work as expected -
the most difficult being the <script> task.
|
XML Configuration
|
In Ant1 configuration of Ant is achieved either through the execution of OS
dependent scripts by the launcher scripts or though properties files which have
the limited capability to set properties.
|
Aspects
|
In Ant1 there are many things which are common to a group of tasks but adding
the capability to each task is repetitive and tedious. An example would be the
failonerror attribute which controls whether a task will cause the build to stop
when it fails. This started on just one task but has since been added to many
other tasks as users want more fine control over when their builds stop. Aspects
have been discussed as a mechanism for providing a single implementation of a
concept or control that can then be applied to tasks independently.
|
|
|