You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

develop.html 23 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. <!--
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. -->
  15. <html>
  16. <head>
  17. <meta http-equiv="Content-Language" content="en-us">
  18. <link rel="stylesheet" type="text/css" href="stylesheets/style.css">
  19. <title>Writing Your Own Task</title>
  20. </head>
  21. <body>
  22. <h1>Developing with Apache Ant</h1>
  23. <h2 id="writingowntask">Writing Your Own Task</h2>
  24. <p>It is very easy to write your own task:</p>
  25. <ol>
  26. <li>Create a Java class that extends <code>org.apache.tools.ant.Task</code>
  27. or <a href="base_task_classes.html">another class</a> that was designed to be extended.</li>
  28. <li id="footnote-1-back">For each attribute, write a <em>setter</em> method. The setter method must be a
  29. <code>public void</code> method that takes a single argument. The
  30. name of the method must begin with <code>set</code>, followed by the
  31. attribute name, with the first character of the name in uppercase, and the rest in
  32. lowercase<a href="#footnote-1"><sup>*</sup></a>. That is, to support an attribute named
  33. <code>file</code> you create a method <code>setFile</code>.
  34. Depending on the type of the argument, Ant will perform some
  35. conversions for you, see <a href="#set-magic">below</a>.</li>
  36. <li>If your task shall contain other tasks as nested elements (like
  37. <a href="Tasks/parallel.html"><code>parallel</code></a>), your
  38. class must implement the interface
  39. <code>org.apache.tools.ant.TaskContainer</code>. If you do so, your
  40. task can not support any other nested elements. See
  41. <a href="#taskcontainer">below</a>.</li>
  42. <li>If the task should support character data (text nested between the
  43. start and end tags), write a <code>public void addText(String)</code>
  44. method. Note that Ant does <strong>not</strong> expand properties on
  45. the text it passes to the task.</li>
  46. <li>For each nested element, write a <em>create</em>, <em>add</em> or
  47. <em>addConfigured</em> method. A create method must be a
  48. <code>public</code> method that takes no arguments and returns an
  49. <code>Object</code> type. The name of the create method must begin
  50. with <code>create</code>, followed by the element name. An add (or
  51. addConfigured) method must be a <code>public void</code> method that
  52. takes a single argument of an <code>Object</code> type with a
  53. no-argument constructor. The name of the add (addConfigured) method
  54. must begin with <code>add</code> (<code>addConfigured</code>),
  55. followed by the element name. For a more complete discussion see
  56. <a href="#nested-elements">below</a>.</li>
  57. <li>Write a <code>public void execute</code> method, with no arguments, that
  58. throws a <code>BuildException</code>. This method implements the task
  59. itself.</li>
  60. </ol>
  61. <hr>
  62. <p id="footnote-1"><a href="#footnote-1-back">*</a> Actually the case of the letters after
  63. the first one doesn't really matter to Ant, using all lower case is a
  64. good convention, though.</p>
  65. <h3>The Life-cycle of a Task</h3>
  66. <ol>
  67. <li>
  68. The xml element that contains the tag corresponding to the
  69. task gets converted to an <code>UnknownElement</code> at parse time.
  70. This <code>UnknownElement</code> gets placed in a list within a target
  71. object, or recursively within another <code>UnknownElement</code>.
  72. </li>
  73. <li>
  74. When the target is executed, each <code>UnknownElement</code> is invoked
  75. using an <code>perform()</code> method. This instantiates
  76. the task. This means that tasks only gets
  77. instantiated at run time.
  78. </li>
  79. <li>The task gets references to its project and location inside the
  80. buildfile via its inherited <code>project</code> and
  81. <code>location</code> variables.</li>
  82. <li>If the user specified an <var>id</var> attribute to this task,
  83. the project registers a reference to this newly created task, at run
  84. time.</li>
  85. <li>The task gets a reference to the target it belongs to via its
  86. inherited <code>target</code> variable.</li>
  87. <li><code>init()</code> is called at run time.</li>
  88. <li>All child elements of the XML element corresponding to this task
  89. are created via this task's <code>createXXX()</code> methods or
  90. instantiated and added to this task via its <code>addXXX()</code>
  91. methods, at run time. Child elements corresponding
  92. to <code>addConfiguredXXX()</code> are created at this point but
  93. the actual <code>addConfigured</code> method is not called.</li>
  94. <li>All attributes of this task get set via their corresponding
  95. <code>setXXX</code> methods, at runtime.</li>
  96. <li>The content character data sections inside the XML element
  97. corresponding to this task is added to the task via its
  98. <code>addText</code> method, at runtime.</li>
  99. <li>All attributes of all child elements get set via their corresponding
  100. <code>setXXX</code> methods, at runtime.</li>
  101. <li>If child elements of the XML element corresponding to this task
  102. have been created for <code>addConfiguredXXX()</code> methods,
  103. those methods get invoked now.</li>
  104. <li id="execute"><code>execute()</code> is called at runtime.
  105. If <code>target1</code> and <code>target2</code> both depend
  106. on <code>target3</code>, then running
  107. <code>'ant target1 target2'</code> will run all tasks in
  108. <code>target3</code> twice.</li>
  109. </ol>
  110. <h3 id="set-magic">Conversions Ant will perform for attributes</h3>
  111. <p>Ant will always expand properties before it passes the value of an
  112. attribute to the corresponding setter method. <em>Since Ant 1.8</em>, it is
  113. possible to <a href="Tasks/propertyhelper.html">extend Ant's property handling</a>
  114. such that a non-string Object may be the result of the evaluation of a string
  115. containing a single property reference. These will be assigned directly via
  116. setter methods of matching type. Since it requires some beyond-the-basics
  117. intervention to enable this behavior, it may be a good idea to flag attributes
  118. intended to permit this usage paradigm.
  119. </p>
  120. <p>The most common way to write an attribute setter is to use a
  121. <code>java.lang.String</code> argument. In this case Ant will pass
  122. the literal value (after property expansion) to your task. But there
  123. is more! If the argument of you setter method is</p>
  124. <ul>
  125. <li><code>boolean</code>, your method will be passed the value
  126. <code>true</code> if the value specified in the build file is one of
  127. <code>true</code>, <code>yes</code>, or <code>on</code> and
  128. <code>false</code> otherwise.</li>
  129. <li><code>char</code> or <code>java.lang.Character</code>, your
  130. method will be passed the first character of the value specified in
  131. the build file.</li>
  132. <li>any other primitive type (<code>int</code>, <code>short</code>
  133. and so on), Ant will convert the value of the attribute into this
  134. type, thus making sure that you'll never receive input that is not a
  135. number for that attribute.</li>
  136. <li><code>java.io.File</code>, Ant will first determine whether the
  137. value given in the build file represents an absolute path name. If
  138. not, Ant will interpret the value as a path name relative to the
  139. project's basedir.</li>
  140. <li><code>org.apache.tools.ant.types.Resource</code>, Ant will
  141. resolve the string as a <code>java.io.File</code> as above, then
  142. pass in as a <code>org.apache.tools.ant.types.resources.FileResource</code>.
  143. <em>Since Ant 1.8</em>
  144. </li>
  145. <li><code>org.apache.tools.ant.types.Path</code>, Ant will tokenize
  146. the value specified in the build file, accepting <q>:</q> and
  147. <q>;</q> as path separators. Relative path names will be
  148. interpreted as relative to the project's <var>basedir</var>.</li>
  149. <li><code>java.lang.Class</code>, Ant will interpret the value
  150. given in the build file as a Java class name and load the named
  151. class from the system class loader.</li>
  152. <li>any other type that has a constructor with a single
  153. <code>String</code> argument, Ant will use this constructor to
  154. create a new instance from the value given in the build file.</li>
  155. <li>A subclass of
  156. <code>org.apache.tools.ant.types.EnumeratedAttribute</code>, Ant
  157. will invoke this classes <code>setValue</code> method. Use this if
  158. your task should support enumerated attributes (attributes with
  159. values that must be part of a predefined set of values). See
  160. <code>org/apache/tools/ant/taskdefs/FixCRLF.java</code> and the
  161. inner <code>AddAsisRemove</code> class used in <code>setCr</code>
  162. for an example.</li>
  163. <li>A (Java 5) enumeration, Ant will call the setter with the enum constant
  164. matching the value given in the build file. This is easier than using
  165. <code>EnumeratedAttribute</code> and can result in cleaner code, but of course
  166. your task will not run on JDK 1.4 or earlier. Note that any override of
  167. <code>toString()</code> in the enumeration is ignored; the build file must use
  168. the declared name (see <code>Enum.getName()</code>). You may wish to use lowercase
  169. enum constant names, in contrast to usual Java style, to look better in build files.
  170. <em>Since Ant 1.7.0</em></li>
  171. </ul>
  172. <p>What happens if more than one setter method is present for a given
  173. attribute? A method taking a <code>String</code> argument will always
  174. lose against the more specific methods. If there are still more
  175. setters Ant could chose from, only one of them will be called, but we
  176. don't know which, this depends on the implementation of your Java
  177. virtual machine.</p>
  178. <h3 id="nested-elements">Supporting nested elements</h3>
  179. <p>Let's assume your task shall support nested elements with the name
  180. <code>inner</code>. First of all, you need a class that represents
  181. this nested element. Often you simply want to use one of Ant's
  182. classes like <code>org.apache.tools.ant.types.FileSet</code> to
  183. support nested <code>fileset</code> elements.</p>
  184. <p>Attributes of the nested elements or nested child elements of them
  185. will be handled using the same mechanism used for tasks (i.e. setter
  186. methods for attributes, addText for nested text and
  187. create/add/addConfigured methods for child elements).</p>
  188. <p>Now you have a class <code>NestedElement</code> that is supposed to
  189. be used for your nested <code>&lt;inner&gt;</code> elements, you have
  190. three options:</p>
  191. <ol>
  192. <li><code>public NestedElement createInner()</code></li>
  193. <li><code>public void addInner(NestedElement anInner)</code></li>
  194. <li><code>public void addConfiguredInner(NestedElement anInner)</code></li>
  195. </ol>
  196. <p>What is the difference?</p>
  197. <p>Option 1 makes the task create the instance of
  198. <code>NestedElement</code>, there are no restrictions on the type.
  199. For the options 2 and 3, Ant has to create an instance of
  200. <code>NestedInner</code> before it can pass it to the task, this
  201. means, <code>NestedInner</code> must have a <code>public</code> no-arg
  202. constructor or a <code>public</code> one-arg constructor
  203. taking a <code>Project</code> class as a parameter.
  204. This is the only difference between options 1 and 2.</p>
  205. <p>The difference between 2 and 3 is what Ant has done to the object
  206. before it passes it to the method. <code>addInner</code> will receive
  207. an object directly after the constructor has been called, while
  208. <code>addConfiguredInner</code> gets the object <em>after</em> the
  209. attributes and nested children for this new object have been
  210. handled.</p>
  211. <p>What happens if you use more than one of the options? Only one of
  212. the methods will be called, but we don't know which, this depends on
  213. the implementation of your JVM.</p>
  214. <h3 id="nestedtype">Nested Types</h3>
  215. If your task needs to nest an arbitrary type that has been defined
  216. using <code>&lt;typedef&gt;</code> you have two options.
  217. <ol>
  218. <li><code>public void add(Type type)</code></li>
  219. <li><code>public void addConfigured(Type type)</code></li>
  220. </ol>
  221. The difference between 1 and 2 is the same as between 2 and 3 in the
  222. previous section.
  223. <p>
  224. For example suppose one wanted to handle objects object of type
  225. <code>org.apache.tools.ant.taskdefs.condition.Condition</code>, one may
  226. have a class:
  227. </p>
  228. <pre>
  229. public class MyTask extends Task {
  230. private List conditions = new ArrayList();
  231. public void add(Condition c) {
  232. conditions.add(c);
  233. }
  234. public void execute() {
  235. // iterator over the conditions
  236. }
  237. }</pre>
  238. <p>
  239. One may define and use this class like this:
  240. </p>
  241. <pre>
  242. &lt;taskdef name="mytask" classname="MyTask" classpath="classes"/&gt;
  243. &lt;typedef name="condition.equals"
  244. classname="org.apache.tools.ant.taskdefs.conditions.Equals"/&gt;
  245. &lt;mytask&gt;
  246. &lt;condition.equals arg1="${debug}" arg2="true"/&gt;
  247. &lt;/mytask&gt;</pre>
  248. <p>
  249. A more complicated example follows:
  250. </p>
  251. <pre>
  252. public class Sample {
  253. public static class MyFileSelector implements FileSelector {
  254. public void setAttrA(int a) {}
  255. public void setAttrB(int b) {}
  256. public void add(Path path) {}
  257. public boolean isSelected(File basedir, String filename, File file) {
  258. return true;
  259. }
  260. }
  261. interface MyInterface {
  262. void setVerbose(boolean val);
  263. }
  264. public static class BuildPath extends Path {
  265. public BuildPath(Project project) {
  266. super(project);
  267. }
  268. public void add(MyInterface inter) {}
  269. public void setUrl(String url) {}
  270. }
  271. public static class XInterface implements MyInterface {
  272. public void setVerbose(boolean x) {}
  273. public void setCount(int c) {}
  274. }
  275. }</pre>
  276. <p>
  277. This class defines a number of static classes that implement/extend
  278. <code>Path</code>, <code>MyFileSelector</code> and <code>MyInterface</code>. These may be defined and used
  279. as follows:
  280. </p>
  281. <pre>
  282. &lt;typedef name="myfileselector" classname="Sample$MyFileSelector"
  283. classpath="classes" loaderref="classes"/&gt;
  284. &lt;typedef name="buildpath" classname="Sample$BuildPath"
  285. classpath="classes" loaderref="classes"/&gt;
  286. &lt;typedef name="xinterface" classname="Sample$XInterface"
  287. classpath="classes" loaderref="classes"/&gt;
  288. &lt;copy todir="copy-classes"&gt;
  289. &lt;fileset dir="classes"&gt;
  290. &lt;myfileselector attra="10" attrB="-10"&gt;
  291. &lt;buildpath path="." url="abc"&gt;
  292. &lt;xinterface count="4"/&gt;
  293. &lt;/buildpath&gt;
  294. &lt;/myfileselector&gt;
  295. &lt;/fileset&gt;
  296. &lt;/copy&gt;</pre>
  297. <h3 id="taskcontainer">TaskContainer</h3>
  298. <p>The <code>TaskContainer</code> consists of a single method,
  299. <code>addTask</code> that basically is the same as an <a
  300. href="#nested-elements">add method</a> for nested elements. The task
  301. instances will be configured (their attributes and nested elements
  302. have been handled) when your task's <code>execute</code> method gets
  303. invoked, but not before that.</p>
  304. <p>When we <a href="#execute">said</a> <code>execute</code> would be
  305. called, we lied ;-). In fact, Ant will call the <code>perform</code>
  306. method in <code>org.apache.tools.ant.Task</code>, which in turn calls
  307. <code>execute</code>. This method makes sure that <a
  308. href="#buildevents">Build Events</a> will be triggered. If you
  309. execute the task instances nested into your task, you should also
  310. invoke <code>perform</code> on these instances instead of
  311. <code>execute</code>.</p>
  312. <h3>Example</h3>
  313. <p>Let's write our own task, which prints a message on the
  314. <code>System.out</code> stream.
  315. The task has one attribute, called <code>message</code>.</p>
  316. <pre>
  317. package com.mydomain;
  318. import org.apache.tools.ant.BuildException;
  319. import org.apache.tools.ant.Task;
  320. public class MyVeryOwnTask extends Task {
  321. private String msg;
  322. // The method executing the task
  323. public void execute() throws BuildException {
  324. System.out.println(msg);
  325. }
  326. // The setter for the &quot;message&quot; attribute
  327. public void setMessage(String msg) {
  328. this.msg = msg;
  329. }
  330. }</pre>
  331. <p>It's really this simple ;-)</p>
  332. <p>Adding your task to the system is rather simple too:</p>
  333. <ol>
  334. <li>Make sure the class that implements your task is in the classpath when
  335. starting Ant.</li>
  336. <li>Add a <code>&lt;taskdef&gt;</code> element to your project.
  337. This actually adds your task to the system.</li>
  338. <li>Use your task in the rest of the buildfile.</li>
  339. </ol>
  340. <h3>Example</h3>
  341. <pre>
  342. &lt;?xml version=&quot;1.0&quot;?&gt;
  343. &lt;project name=&quot;OwnTaskExample&quot; default=&quot;main&quot; basedir=&quot;.&quot;&gt;
  344. &lt;taskdef name=&quot;mytask&quot; classname=&quot;com.mydomain.MyVeryOwnTask&quot;/&gt;
  345. &lt;target name=&quot;main&quot;&gt;
  346. &lt;mytask message=&quot;Hello World! MyVeryOwnTask works!&quot;/&gt;
  347. &lt;/target&gt;
  348. &lt;/project&gt;</pre>
  349. <h3>Example 2</h3>
  350. To use a task directly from the buildfile which created it, place the
  351. <code>&lt;taskdef&gt;</code> declaration inside a target
  352. <em>after the compilation</em>. Use the <var>classpath</var> attribute of
  353. <code>&lt;taskdef&gt;</code> to point to where the code has just been
  354. compiled.
  355. <pre>
  356. &lt;?xml version=&quot;1.0&quot;?&gt;
  357. &lt;project name=&quot;OwnTaskExample2&quot; default=&quot;main&quot; basedir=&quot;.&quot;&gt;
  358. &lt;target name=&quot;build&quot; &gt;
  359. &lt;mkdir dir=&quot;build&quot;/&gt;
  360. &lt;javac srcdir=&quot;source&quot; destdir=&quot;build&quot;/&gt;
  361. &lt;/target&gt;
  362. &lt;target name=&quot;declare&quot; depends=&quot;build&quot;&gt;
  363. &lt;taskdef name=&quot;mytask&quot;
  364. classname=&quot;com.mydomain.MyVeryOwnTask&quot;
  365. classpath=&quot;build&quot;/&gt;
  366. &lt;/target&gt;
  367. &lt;target name=&quot;main&quot; depends=&quot;declare&quot;&gt;
  368. &lt;mytask message=&quot;Hello World! MyVeryOwnTask works!&quot;/&gt;
  369. &lt;/target&gt;
  370. &lt;/project&gt;</pre>
  371. <p>Another way to add a task (more permanently) is to add the task name and
  372. implementing class name to the <samp>default.properties</samp> file in the
  373. <code>org.apache.tools.ant.taskdefs</code>
  374. package. Then you can use it as if it were a built-in task.</p>
  375. <hr>
  376. <h2 id="buildevents">Build Events</h2>
  377. <p>Ant is capable of generating build events as it performs the tasks necessary to build a project.
  378. Listeners can be attached to Ant to receive these events. This capability could be used, for example,
  379. to connect Ant to a GUI or to integrate Ant with an IDE.
  380. </p>
  381. <p>To use build events you need to create an ant <code>Project</code> object. You can then call the
  382. <code>addBuildListener</code> method to add your listener to the project. Your listener must implement
  383. the <code>org.apache.tools.antBuildListener</code> interface. The listener will receive BuildEvents
  384. for the following events</p>
  385. <ul>
  386. <li>Build started</li>
  387. <li>Build finished</li>
  388. <li>Target started</li>
  389. <li>Target finished</li>
  390. <li>Task started</li>
  391. <li>Task finished</li>
  392. <li>Message logged</li>
  393. </ul>
  394. <p>If the build file invokes another build file via
  395. <a href="Tasks/ant.html"><code>&lt;ant&gt;</code></a> or
  396. <a href="Tasks/subant.html"><code>&lt;subant&gt;</code></a> or uses
  397. <a href="Tasks/antcall.html"><code>&lt;antcall&gt;</code></a>, you are creating a
  398. new Ant "project" that will send target and task level events of its
  399. own but never sends build started/finished events. <em>Since Ant 1.6.2</em>,
  400. BuildListener interface has an extension named
  401. SubBuildListener that will receive two new events for</p>
  402. <ul>
  403. <li>SubBuild started</li>
  404. <li>SubBuild finished</li>
  405. </ul>
  406. <p>If you are interested in those events, all you need to do is to
  407. implement the new interface instead of BuildListener (and register the
  408. listener, of course).</p>
  409. <p>If you wish to attach a listener from the command line you may use the
  410. <code>-listener</code> option. For example:</p>
  411. <pre>ant -listener org.apache.tools.ant.XmlLogger</pre>
  412. <p>will run Ant with a listener that generates an XML representation of the build progress. This
  413. listener is included with Ant, as is the default listener, which generates the logging to standard output.</p>
  414. <p><strong>Note</strong>: A listener must not access <code>System.out</code> and <code>System.err</code> directly since output on
  415. these streams is redirected by Ant's core to the build event system. Accessing these
  416. streams can cause an infinite loop in Ant. Depending on the version of Ant, this will
  417. either cause the build to terminate or the JVM to run out of Stack space. A logger, also, may
  418. not access System.out and System.err directly. It must use the streams with which it has
  419. been configured.</p>
  420. <p><strong>Note</strong>: All methods of a BuildListener except for the "Build
  421. Started" and "Build Finished" events may occur on several threads
  422. simultaneously&mdash;for example while Ant is executing
  423. a <code>&lt;parallel&gt;</code> task.</p>
  424. <h3>Example</h3>
  425. Writing an adapter to your favourite log library is very easy.
  426. Just implement the BuildListener interface, instantiate your logger and delegate
  427. the message to that instance.<br/>
  428. When starting your build provide your adapter class and the log library to the
  429. build classpath and activate your logger via <code>-listener</code> option as
  430. described above.
  431. <pre>
  432. public class MyLogAdapter implements BuildListener {
  433. private MyLogger getLogger() {
  434. final MyLogger log = MyLoggerFactory.getLogger(Project.class.getName());
  435. return log;
  436. }
  437. @Override
  438. public void buildStarted(final BuildEvent event) {
  439. final MyLogger log = getLogger();
  440. log.info("Build started.");
  441. }
  442. @Override
  443. public void buildFinished(final BuildEvent event) {
  444. final MyLogger logger = getLogger();
  445. MyLogLevelEnum loglevel = ... // map event.getPriority() to enum via Project.MSG_* constants
  446. boolean allOK = event.getException() == null;
  447. String logmessage = ... // create log message using data of the event and the message invoked
  448. logger.log(loglevel, logmessage);
  449. }
  450. // implement all methods in that way
  451. }</pre>
  452. <hr>
  453. <h2 id="integration">Source code integration</h2>
  454. <p>The other way to extend Ant through Java is to make changes to existing tasks, which is positively encouraged.
  455. Both changes to the existing source and new tasks can be incorporated back into the Ant codebase, which
  456. benefits all users and spreads the maintenance load around.</p>
  457. <p>Please consult the
  458. <a href="https://www.apache.org/foundation/getinvolved.html">Getting Involved</a> pages on the Apache web site
  459. for details on how to fetch the latest source and how to submit changes for reincorporation into the
  460. source tree.</p>
  461. <p>Ant also has some
  462. <a href="https://ant.apache.org/ant_task_guidelines.html">task guidelines</a>
  463. which provides some advice to people developing and testing tasks. Even if you intend to
  464. keep your tasks to yourself, you should still read this as it should be informative.</p>
  465. </body>
  466. </html>