Ant Macros & Presets
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against Apache Ant 1.10.18. Every build file and every console output below comes from a real run of
that release on JDK 26 on 2026-10-16, in a scratch copy of the tidepool project; the project directory is shortened to
/work/mp in the output and the closing Total time line is omitted. Features newer than Ant 1.6 name their release in the
text.
A target is a node in a graph and cannot take arguments (see
Targets & Extension Points and the
trap of antcall). When a build needs the opposite, a reusable step that takes parameters and can be used any number of
times, Ant offers macros. A macro is a new task defined from existing tasks, so using one looks exactly like using
javac or echo. Four tasks make the family:
-
<macrodef>defines a task from a<sequential>template with attributes, nested elements and nested text. -
<presetdef>defines a task or type from another one by presetting some of its attributes and nested elements. -
<componentdef>defines a nested component (a condition, a selector) rather than a task. -
<augment>modifies an existing reference (afileset, apath) by adding attributes or nested elements.
Source: Ant manual: MacroDef task
Source: Ant manual: PreSetDef task
macrodef
A first macro for the tidepool modules
Compiling a module is the same call with different directories and class paths three times in the tidepool build. A macro states it once:
<project name="tidepool" default="all" basedir=".">
<property name="lib.dir" location="lib"/>
<macrodef name="compile-module" description="javac one tidepool module into its build/classes">
<attribute name="module"/>
<attribute name="release" default="21"/>
<attribute name="classes" default="@{module}/build/classes"/>
<element name="extra-classpath" optional="true"/>
<sequential>
<mkdir dir="@{classes}"/>
<javac srcdir="@{module}/src/main/java" destdir="@{classes}"
release="@{release}" includeantruntime="false">
<classpath>
<extra-classpath/>
</classpath>
</javac>
</sequential>
</macrodef>
<target name="all">
<compile-module module="tidepool-model"/>
<compile-module module="tidepool-engine">
<extra-classpath>
<pathelement location="tidepool-model/build/classes"/>
<fileset dir="${lib.dir}" includes="commons-csv-*.jar"/>
</extra-classpath>
</compile-module>
<compile-module module="tidepool-cli" release="17">
<extra-classpath>
<pathelement location="tidepool-model/build/classes"/>
<pathelement location="tidepool-engine/build/classes"/>
<fileset dir="${lib.dir}" includes="*.jar"/>
</extra-classpath>
</compile-module>
</target>
</project>
Buildfile: /work/mp/macro1.xml
all:
[mkdir] Created dir: /work/mp/tidepool-model/build/classes
[javac] Compiling 4 source files to /work/mp/tidepool-model/build/classes
[mkdir] Created dir: /work/mp/tidepool-engine/build/classes
[javac] Compiling 2 source files to /work/mp/tidepool-engine/build/classes
[mkdir] Created dir: /work/mp/tidepool-cli/build/classes
[javac] Compiling 1 source file to /work/mp/tidepool-cli/build/classes
BUILD SUCCESSFUL
What the definition shows:
-
<attribute>declares an attribute of the new task. Without adefaultit is required. A default may use an attribute declared before it (classesuses@{module}). -
<element>declares a nested element. Whatever the caller writes inside<extra-classpath>is pasted where the template contains<extra-classpath/>. Here the template wraps it in a<classpath>, so the caller supplies only path elements. -
<sequential>is the template: an ordinary list of tasks run in order. Attribute values replace@{name}wherever it appears in the template, including in nested elements and inside other attribute values. -
The macro is a task. The log lines are labelled by the inner tasks (
mkdir,javac); the macro itself adds no line. -
The
descriptionis documentation only; Ant does not use it.
Source: Ant manual: MacroDef task
The attributes of macrodef
| Attribute | Meaning | Default |
|---|---|---|
|
The name of the new task. Required. |
|
|
The namespace URI the definition lives in. A task defined with |
none |
|
Documentation only. |
none |
|
Whether an error inside the macro prints the stack of macro calls (since Ant 1.7). |
|
The nested elements
| Element | Meaning | Attributes |
|---|---|---|
|
A named parameter, substituted as |
|
|
A named slot the caller fills with nested tasks or types. The case of the name is ignored. |
|
|
The text content of the call becomes an attribute of this name (since Ant 1.6.1). Without a |
|
Optional, implicit and text elements
An optional element may be omitted. A required element (the default) that the caller leaves out is an error:
<macrodef name="needs-body">
<element name="body"/>
<sequential><body/></sequential>
</macrodef>
<target name="nobody"><needs-body/></target>
nobody:
BUILD FAILED
/work/mp/more.xml:27: Required nested element body missing
An implicit element has no wrapper: everything the caller writes inside the macro goes into the slot. There can only be one implicit element. A text element turns the body of the call into an attribute, optionally trimmed:
<macrodef name="banner">
<attribute name="char" default="*"/>
<text name="message" trim="true"/>
<sequential>
<echo>@{char}@{char} @{message} @{char}@{char}</echo>
</sequential>
</macrodef>
<macrodef name="for-each-file">
<attribute name="dir"/>
<element name="do" implicit="true"/>
<sequential>
<echo>scanning @{dir}</echo>
<do/>
</sequential>
</macrodef>
<target name="go">
<banner>
tidepool 1.0.0
</banner>
<banner char="#"> stations: ${stations} </banner>
<for-each-file dir="data">
<echo>implicit element body</echo>
<echo>second task</echo>
</for-each-file>
</target>
Run with ant -Dstations=3:
go:
[echo] ** tidepool 1.0.0 **
[echo] ## stations: 3 ##
[echo] scanning data
[echo] implicit element body
[echo] second task
BUILD SUCCESSFUL
The trimmed text lost its surrounding whitespace and newlines, and ${stations} in the body was expanded before the macro
saw it. (The macro named for-each-file does not iterate: it only wraps a body. Ant has no loop, see the last section.)
Errors a caller can make
Using a macro wrongly gives precise messages:
<macrodef name="greet">
<attribute name="who"/>
<attribute name="greeting" default="Hello"/>
<sequential>
<echo>@{greeting}, @{who}! (Who=@{WHO})</echo>
</sequential>
</macrodef>
| Call | Result |
|---|---|
|
|
|
|
|
|
|
|
(The line numbers are those of the scratch file; the messages are exactly as Ant printed them.) The location in each message is the call site, which is where the mistake is.
Source: Apache Ant manual: MacroDef
backtrace
When a task inside a macro fails, Ant by default shows both the call site and the line inside the macro, so that the failure can be traced:
<macrodef name="check-loud">
<attribute name="file"/>
<sequential>
<fail message="missing: @{file}"><condition><not><available file="@{file}"/></not></condition></fail>
</sequential>
</macrodef>
<macrodef name="check-quiet" backtrace="false">
<attribute name="file"/>
<sequential>
<fail message="missing: @{file}"><condition><not><available file="@{file}"/></not></condition></fail>
</sequential>
</macrodef>
<target name="loud"><check-loud file="nope.csv"/></target>
<target name="quiet"><check-quiet file="nope.csv"/></target>
$ ant loud
loud:
BUILD FAILED
/work/mp/bt.xml:14: The following error occurred while executing this line:
/work/mp/bt.xml:5: missing: nope.csv
$ ant quiet
quiet:
BUILD FAILED
/work/mp/bt.xml:15: missing: nope.csv
backtrace="false" is for macros that are a deliberate, user-facing check: the error should point at the caller’s line
only, not at the internals of the macro.
@{} versus ${}: how a macro is expanded
Two kinds of substitution happen in a macro and the order matters.
-
${name}is the property expansion, done by Ant when a task is configured at run time. -
@{name}is the macro attribute substitution, done when the macro is called. It works on the template text, before the inner tasks run. Use@@{name}to write a literal@{name}, just as$${name}writes a literal${name}.
Because the attribute is substituted into the template and the template is then expanded as properties, a value that
contains a property reference is expanded twice by default. The Ant FAQ describes it: an attribute of $${basedir}
ends up printing the value of basedir and not the text ${basedir} that one expects. Since Ant 1.8.3 the attribute
can switch the second expansion off with doubleexpanding="false". Escapes, defaults and double expansion, run together:
<macrodef name="show">
<attribute name="text"/>
<sequential><echo>twice : @{text}</echo></sequential>
</macrodef>
<macrodef name="show-once">
<attribute name="text" doubleexpanding="false"/>
<sequential><echo>once : @{text}</echo></sequential>
</macrodef>
<macrodef name="esc">
<attribute name="x" default="v"/>
<sequential><echo>literal @@{x} versus @{x}, dollar $${x}</echo></sequential>
</macrodef>
<macrodef name="deflt">
<attribute name="two" default="@{one}"/>
<attribute name="one"/>
<sequential><echo>one=@{one} two=@{two}</echo></sequential>
</macrodef>
<macrodef name="deflt2">
<attribute name="one"/>
<attribute name="two" default="@{one}"/>
<sequential><echo>one=@{one} two=@{two}</echo></sequential>
</macrodef>
<target name="go">
<show text="$${basedir}"/>
<show-once text="$${basedir}"/>
<esc/>
<deflt one="test"/>
<deflt2 one="test"/>
</target>
go:
[echo] twice : /work/mp
[echo] once : ${basedir}
[echo] literal @{x} versus v, dollar ${x}
[echo] one=test two=@{one}
[echo] one=test two=test
BUILD SUCCESSFUL
The five lines show, in order: the double expansion (the value of basedir), doubleexpanding="false" (the literal
text), the @@{x} escape, a default that refers to an attribute declared after it (two=@{one}, because one was not
yet set), and the same default declared before it (two=test). The rule for defaults is therefore "declare the
attribute you refer to first".
The double expansion is deliberate. It is what makes this idiom possible, a nested property name built from a macro
attribute, which ${thing.${choice}} cannot do in plain Ant (the FAQ’s propertycopy example is the same idea):
<property name="choice" value="2"/>
<property name="thing.1" value="one"/>
<property name="thing.2" value="two"/>
<macrodef name="pick">
<attribute name="n"/>
<sequential><echo>thing @{n} = ${thing.@{n}}</echo></sequential>
</macrodef>
<target name="go">
<echo>direct nesting: ${thing.${choice}}</echo>
<pick n="1"/>
<pick n="${choice}"/>
</target>
go:
[echo] direct nesting: ${thing.${choice}}
[echo] thing 1 = one
[echo] thing 2 = two
BUILD SUCCESSFUL
The first line shows that the plain nesting is not expanded; the macro does it. A macro that passes untrusted text or text
from a file through to a task should use doubleexpanding="false" so that a stray $ in the data is not interpreted.
local inside a macro
Properties are global and immutable (see Properties), so a
macro that sets a property to hold an intermediate value has a classic bug: the first call defines it for the rest of the
build and every later call silently keeps the first value. The <local> task declares a property that is visible only in
the enclosing scope, and a macro body is a scope:
<macrodef name="stamp-bad">
<attribute name="module"/>
<sequential>
<property name="out" value="@{module}.out"/>
<echo>bad : @{module} writes ${out}</echo>
</sequential>
</macrodef>
<macrodef name="stamp-good">
<attribute name="module"/>
<sequential>
<local name="out"/>
<property name="out" value="@{module}.out"/>
<echo>good: @{module} writes ${out}</echo>
</sequential>
</macrodef>
<target name="go">
<stamp-bad module="model"/>
<stamp-bad module="engine"/>
<stamp-good module="model"/>
<stamp-good module="engine"/>
<echo>after the macros: out=${out}</echo>
</target>
go:
[echo] bad : model writes model.out
[echo] bad : engine writes model.out
[echo] good: model writes model.out
[echo] good: engine writes engine.out
[echo] after the macros: out=model.out
BUILD SUCCESSFUL
The second stamp-bad still reports model.out: the property from the first call won. The good calls each got their own
out, shadowing the global one that the bad macro had left behind. local has been available since Ant 1.8.0; before it,
the usual workaround was to build the property name from an attribute, <property name="@{module}.out" .../>.
Alternatively avoid the property and pass the value straight into the nested tasks with @{...}, as compile-module does.
Source: Ant manual: Local task
Macros in shared files and in namespaces
A macro can live in an imported file; once the import has been processed it is available in the whole project. A
uri puts it in a namespace so that names cannot clash with other libraries or with built-in tasks:
<project name="tp-macros" xmlns:if="ant:if" xmlns:unless="ant:unless">
<macrodef name="check-file" uri="urn:tidepool" description="fail unless a file exists">
<attribute name="file"/>
<attribute name="strict" default="true"/>
<sequential>
<echo>checking @{file}</echo>
<fail message="missing: @{file}" if:true="@{strict}">
<condition><not><available file="@{file}"/></not></condition>
</fail>
<echo unless:true="@{strict}">lenient mode: @{file} is optional</echo>
</sequential>
</macrodef>
</project>
<project name="use" default="go" xmlns:tp="urn:tidepool">
<import file="tp-macros.xml"/>
<target name="define-late">
<macrodef name="late"><sequential><echo>defined inside a target, visible afterwards</echo></sequential></macrodef>
</target>
<target name="go" depends="define-late">
<tp:check-file file="data/stations.csv"/>
<tp:check-file file="data/tides.csv" strict="false"/>
<late/>
</target>
</project>
define-late:
go:
[echo] checking data/stations.csv
[echo] checking data/tides.csv
[echo] lenient mode: data/tides.csv is optional
[echo] defined inside a target, visible afterwards
BUILD SUCCESSFUL
With strict="false" removed (so the default true applies) the second check fails and the error names both files:
BUILD FAILED
/work/mp/use.xml:10: The following error occurred while executing this line:
/work/mp/tp-macros.xml:7: missing: data/tides.csv
Three things to note. The if:true and unless:true attributes (the ant:if and ant:unless namespaces, Ant 1.9.1) are
evaluated after @{strict} has been substituted, so a macro attribute can switch a nested task on and off (see
Targets & Extension Points). A macro
defined inside a target is visible to the rest of the project after that target has run, not only in the target, so the
definition is global, as for any other definition task. And the best home for macros that many builds share is an antlib
(a JAR with an antlib.xml that holds the macrodef and presetdef entries), loaded through a namespace URI such as
antlib:com.example.tidepool; see Antlibs & Namespaces.
Source: Apache Ant manual: MacroDef and Import
Redefinition
A second definition of the same name replaces the first, and Ant says so:
Trying to override old definition of task hello
go:
[echo] two
BUILD SUCCESSFUL
The message appears at the default log level, before the targets run. The same happens when a macro is named like a
built-in task. A macro named echo that uses echo in its body calls itself, not the built-in one; In a test the redefined echo called itself and failed with
The "echo" macro does not support nested text data. Give macros
distinct names, or put them in a namespace.
Source: Ant manual: MacroDef task
presetdef
<presetdef> creates a new task or type from an existing one with some attributes and nested elements fixed. The preset
values are ordinary defaults: the caller can override any attribute, and add nested elements. Presets are the right tool
when the "macro" would only forward every attribute unchanged, as with a javac that always uses the same release and
compiler flags.
<project name="preset" default="go" xmlns:tp="urn:tidepool">
<property name="classes.dir" location="build/preset-classes"/>
<presetdef name="tp.javac">
<javac release="21" includeantruntime="false" debug="true" destdir="${classes.dir}">
<compilerarg value="-Xlint:all"/>
</javac>
</presetdef>
<presetdef name="jar" uri="urn:tidepool">
<jar compress="false" level="0"/>
</presetdef>
<target name="go">
<mkdir dir="${classes.dir}"/>
<tp.javac srcdir="tidepool-model/src/main/java"/>
<tp.javac srcdir="tidepool-engine/src/main/java" debug="false">
<classpath>
<pathelement location="${classes.dir}"/>
<fileset dir="lib" includes="commons-csv-*.jar"/>
</classpath>
</tp.javac>
<tp:jar destfile="build/preset.jar" basedir="${classes.dir}"/>
</target>
</project>
go:
[mkdir] Created dir: /work/mp/build/preset-classes
[tp.javac] Compiling 4 source files to /work/mp/build/preset-classes
[tp.javac] Compiling 2 source files to /work/mp/build/preset-classes
[tp:jar] Building jar: /work/mp/build/preset.jar
BUILD SUCCESSFUL
The task is logged under its new name. The first use relies on the preset only, the second overrides debug and adds a
nested <classpath>, and the third task is a namespaced jar. Inspecting the JAR with unzip -v shows the entries as
Stored, i.e. the preset compress="false" took effect.
A name given to presetdef may reuse the name of an existing task only in a namespace, because otherwise the definition
replaces the built-in (a practice that surprises later readers). The example uses the dotted name tp.javac, which is
legal as a task name, and the namespaced jar.
Source: Apache Ant manual: PreSetDef
When the properties are evaluated
The values in a preset are evaluated each time the definition is used, not when it is defined. This is deliberate and
differs from macrodef's attribute substitution at call time:
<presetdef name="showmessage">
<echo>message is '${message}'</echo>
</presetdef>
<target name="go">
<showmessage/>
<property name="message" value="Message 1"/>
<showmessage/>
<antcall target="called"><param name="message" value="Message 2"/></antcall>
</target>
<target name="called"><showmessage/></target>
go:
[showmessage] message is '${message}'
[showmessage] message is 'Message 1'
called:
[showmessage] message is 'Message 2'
BUILD SUCCESSFUL
The manual shows a trick to fix the value at definition time: wrap the presetdef in a macro and pass the property as an
attribute, because @{...} is substituted when the macro is called:
<macrodef name="showmessage-presetdef">
<attribute name="messageval"/>
<sequential>
<presetdef name="showmessage">
<echo>message is '@{messageval}'</echo>
</presetdef>
</sequential>
</macrodef>
<target name="trick">
<property name="message" value="at definition"/>
<showmessage-presetdef messageval="${message}"/>
<showmessage/>
<antcall target="called"><param name="message" value="in the call"/></antcall>
</target>
trick:
[showmessage] message is 'at definition'
called:
[showmessage] message is 'at definition'
BUILD SUCCESSFUL
The called target saw the value from the definition, not the <param>. This helps when the preset is used in child builds
started with inheritAll="false", which would not see a property.
Source: Ant manual: PreSetDef task
macrodef or presetdef?
<macrodef> |
<presetdef> |
|
|---|---|---|
What it wraps |
a list of tasks ( |
exactly one task or type |
Parameters |
declared: attributes, elements, text |
none: the original’s attributes and elements, overridable |
Typical use |
a multi-step operation with arguments ( |
a standard configuration of one task (the house |
Property evaluation |
when the macro runs (and again after |
when the preset is used |
Since |
Ant 1.6 (several attributes later) |
Ant 1.6 |
Use a preset whenever the new task would only repeat the attributes of one existing task; use a macro when there is more than one step or when the template needs a computed value.
componentdef
<componentdef> is a typedef with two restrictions: the component can only be used inside other types or tasks that
accept it, and several components may share a name if they implement different interfaces. Ant itself uses this so that
<or> can be both a condition and a selector. A component is therefore never a top-level task:
<componentdef name="either" classname="org.apache.tools.ant.taskdefs.condition.Or"/>
...
<condition property="found">
<either>
<available file="data/stations.csv"/>
<available file="data/other.csv"/>
</either>
</condition>
and the same component used as a task fails:
<target name="go">
<either><istrue value="true"/></either>
</target>
go:
BUILD FAILED
/work/mp/comp2.xml:4: Problem: failed to create task or type either
Cause: The name is undefined.
Action: Check the spelling.
Action: Check that any custom tasks/types have been declared.
Action: Check that any <presetdef>/<macrodef> declarations have taken place.
Used inside <condition> as above, the component printed found=true (the build file had <echo>found=${found}</echo> after
the condition). Use componentdef
for a custom condition or selector class that should share a name with another one; for everything else use typedef
or an antlib (see Custom Types, Conditions & Selectors).
A wrong class name fails when the definition is read: componentdef class ... cannot be found.
Source: Ant manual: ComponentDef task
augment
<augment> (since Ant 1.8.1) reloads a previously declared reference by its id and treats the augment element as if it
were the original element. It can add attributes (replacing them) and nested elements. It throws a build exception when no
reference of that id exists. It is useful for adding to a type a shared file defined without editing that file.
<fileset id="sources" dir="tidepool-model/src/main/java" includes="**/*.java"/>
<augment id="sources" excludes="**/Height.java"/>
<augment id="sources">
<filename name="**/S*.java"/>
</augment>
<target name="go">
<pathconvert property="listing" refid="sources" pathsep=" ">
<mapper type="flatten"/>
</pathconvert>
<echo>sources: ${listing}</echo>
</target>
go:
[echo] sources: Station.java
BUILD SUCCESSFUL
Without the two augment elements the same build prints sources: Constituent.java Height.java Station.java TideEvent.java.
The four model sources were reduced to one: the first augment set excludes, which removed Height.java, and the second
added a <filename> selector that keeps only names starting with S. In a shared common.xml, a module can define a path
or fileset with a given id and a later augment in the importing file extends it.
Source: Ant manual: Augment task
Replacing antcall and Ant-Contrib
Two habits from older builds have direct replacements.
antcall as a function. The two builds below print the same greeting twice. The first calls a target through antcall,
the second calls a macro:
<target name="init"><echo>init</echo></target>
<target name="greet-target" depends="init"><echo>hello ${who}</echo></target>
<target name="with-antcall" depends="init">
<antcall target="greet-target"><param name="who" value="tide"/></antcall>
<antcall target="greet-target"><param name="who" value="moon"/></antcall>
</target>
<macrodef name="greet">
<attribute name="who"/>
<sequential><echo>hello @{who}</echo></sequential>
</macrodef>
<target name="with-macro" depends="init">
<greet who="tide"/>
<greet who="moon"/>
</target>
$ ant with-antcall
init:
[echo] init
with-antcall:
init:
[echo] init
greet-target:
[echo] hello tide
init:
[echo] init
greet-target:
[echo] hello moon
BUILD SUCCESSFUL
$ ant with-macro
init:
[echo] init
with-macro:
[echo] hello tide
[echo] hello moon
BUILD SUCCESSFUL
antcall started a new project twice and ran init three times in total; the macro ran in the same project, once.
Ant-Contrib. Many build files downloaded from the internet use the <if>, <for>, <foreach>, <var> and
<propertycopy> tasks of Ant-Contrib, a third-party task library. It is listed on the Ant projects page but dormant
(the last release is 1.0b3), so new builds should avoid it. Core Ant covers most of what it was used for:
| Ant-Contrib idiom | Core Ant replacement |
|---|---|
|
|
|
|
|
a macro attribute and |
|
a macro called once per item; |
|
|
|
a child build that writes a properties file, loaded with |
The iteration idioms are on Conditions & Control Flow
and scripting is on Scripting; the subant forms are on
Multi-File Builds.
What a macro cannot do, such as real exception handling or computed values, belongs in <script> or a custom task
(Writing Custom Tasks).
The same ideas in Maven and Gradle
Maven has no build-file-level macros. Reuse is built into the model: a parent POM carries shared plugin configuration and
version management, a plugin is the unit of reusable logic, and an antrun execution can hold a macrodef when a
step needs Ant. Gradle builds are programs, so the language supplies the reuse: functions, classes and task types.
| Ant | Maven | Gradle (Kotlin DSL) |
|---|---|---|
|
a plugin goal with parameters ( |
a function, or a task type ( |
|
|
|
|
inheritance of a |
|
|
rerun a phase |
not available: tasks run at most once per build |
a macro in an antlib |
a plugin JAR |
a convention plugin in |
See Maven: Plugins & Their Configuration and Maven: Writing Plugins for the Maven side, and Gradle: Writing Custom Tasks and Gradle: Tasks for Gradle. The overall comparison is on Comparing Ant, Maven & Gradle.
Source: Gradle: Writing custom tasks
Pitfalls and good practice
-
Name macros so they do not collide. A macro with the name of a built-in task replaces it for the whole project (Ant prints
Trying to override old definition of task ...). Use a prefix, a dotted name or auri. -
Declare
@{...}defaults in the order of use. A default can refer only to an attribute declared before it. -
Use
<local>for intermediate values in a macro, or the second call silently keeps the first call’s value. -
Remember the double expansion. Data that may contain
$goes in adoubleexpanding="false"attribute. -
Remember the scope of a definition. A macro defined in a target is global once that target has run, and in an imported file it is global after the import.
-
Choose
presetdeffor one task,macrodeffor several. -
Keep
descriptionon public macros, andbacktrace="false"only for deliberate checks. -
Do not use
antcallas a function call. It re-runsdependsin a new project; use a macro. -
Avoid Ant-Contrib in new builds; it is dormant and the core replacements above are shorter.
Where to go next
-
Multi-File Builds: import, include & subant for sharing macros and targets across files.
-
Antlibs & Namespaces for shipping macros and presets as a JAR.
-
Scripting when a loop or computation is needed.
-
Writing Custom Tasks for what a macro cannot do.