You are viewing a plain text version of this content. The canonical link for it is here.
Posted to easyant-commits@incubator.apache.org by jl...@apache.org on 2012/10/01 18:31:13 UTC

svn commit: r1392501 - /incubator/easyant/core/trunk/src/documentation/howto/writePlugins.html

Author: jlboudart
Date: Mon Oct  1 18:31:13 2012
New Revision: 1392501

URL: http://svn.apache.org/viewvc?rev=1392501&view=rev
Log:
Update writting plugins tutorial

Modified:
    incubator/easyant/core/trunk/src/documentation/howto/writePlugins.html

Modified: incubator/easyant/core/trunk/src/documentation/howto/writePlugins.html
URL: http://svn.apache.org/viewvc/incubator/easyant/core/trunk/src/documentation/howto/writePlugins.html?rev=1392501&r1=1392500&r2=1392501&view=diff
==============================================================================
--- incubator/easyant/core/trunk/src/documentation/howto/writePlugins.html (original)
+++ incubator/easyant/core/trunk/src/documentation/howto/writePlugins.html Mon Oct  1 18:31:13 2012
@@ -44,8 +44,8 @@ myplugin
 </code>
 That's all !
 We've a ready to use plugin structure.
-<code type="shell">
 <!--replace me by an image -->
+<code type="shell">
 |-- module.ivy
 `-- src
     |-- main
@@ -53,14 +53,11 @@ We've a ready to use plugin structure.
     |       `-- myplugin.ant
     `-- test
         `-- antunit
-            |-- common
-            |   `-- test-utils.ant
             `-- myplugin-test.xml
-
 </code>
 
-<h2>Ant script</h2>
-The skeleton has generated the plugin main script in src/main/resources/[MYPLUGIN].ant
+<h2>Ant file</h2>
+The skeleton has generated the plugin main file in src/main/resources/[MYPLUGIN].ant
 <code type="xml">
 <project name="org.mycompany;myplugin" 
         xmlns:ivy="antlib:org.apache.ivy.ant" 
@@ -70,13 +67,10 @@ The skeleton has generated the plugin ma
     <!-- <ea:core-version requiredrevision="[0.7,+]" /> -->
 
     <!-- Sample init target -->
-    <target name=":init" phase="validate">
+    <target name="myplugin:init">
         <!-- you should remove this echo message -->
         <echo level="debug">This is the init target of myplugin</echo>
     </target>
-    
-    <!-- define a generic default target for this plugin -->
-    <target name="doit" depends="validate"/>
 </project>
 </code>
 By convention, projectname of the plugin should be formed like 
@@ -88,37 +82,42 @@ Example: 
 org.mycompany#myplugin
 </code>
 
-<h3>Understanding Phases</h3>
-Phases are high-level build activities, like "package" or "documentation".  Plugins typically add low-level tasks to one or more phases. For example, a plugin might add a "build jar" task to the "package" phase, or a "generate javadoc" task to the "documentation" phase.  Less typically, a plugin can also define new phases for other plugins to use.
-In standard build types the project-lifecycle is defined by a plugin named <a href="../ref/plugins/phases-std.html">phases-std</a>.
+<h3>Understanding extension-point</h3>
+Extension-points are plugins hooks.  Plugins typically add low-level tasks to one or more extension-points. For example, a plugin can contribute to processing sources before compilation, you will in that case plug your own target to "abstract-compile:compile-ready" extension-point". This plugin hooks is defined in abstract-compile plugin".
 
-<h3>Pre conditions</h3>
-A build module should always check that a set of pre conditions is met in the validate phase (for static pre conditions) or at execution (for dynamic pre conditions).
-By convention, this target should be named ":init" and associated to the "validate" phase.
-
-Pre conditions, including for example - checking the existence of a file or a directory, could be performed inside this target. Additionally, this target is a great place to do global initializations that are needed for the rest of the build. This could include a taskdef initialization.
-Pre conditions can be performed by using <a href="../ref/anttasks/Parametertask.html">parameter task</a>.
-Example :
+So we need to import this plugin and plug our own target on it.
 <code type="xml">
-<target name=":init" phase="validate">
-    <!-- Our plugin need at least the existance of "validate" phase" -->
-    <ea:parameter phase="validate" />
-        <ea:parameter property="username" required="false" description="the username used to display en 'hello Username' by calling :hello target"/>
+<ea:plugin module="abstract-compile" revision="0.9"/>
+<target name="myplugin:mytarget" extensionOf="abstract-compile:compile-ready">
+...//your stuff here
 </target>
 </code>
 
+Less typically, a plugin can also define new extension-points for other plugins to use. We highly recommend in that case to create an "abstract" plugin defining common stuff and extension-points to limit coupling between plugins and make them more flexible.
+
+In standard build types the project-lifecycle is defined by a plugin named phases-std. This plugin loads the default lifecycle containing a set of high level extensionPoints like compile,package.
+It's build types responsability to import this plugin and and do the binding between targets and extension-points from this lifecycle.
+
 <h3>Target Naming Conventions</h3>
+By default, all targets should be prefix by the plugin name. In our example "init" target is prefixed by "myplugin".
+
 There is a conventional difference in the way public and private targets are named in Easyant. A <i>public target</i> is one that makes sense for the end user to be aware of, while a <i>private target</i> should be hidden from the end user.
 
-Conventionally, a public target should always have an associated 'description' attribute. Further, it's name should always begin with a ':'. 
+Conventionally, 
+<ul>
+  <li>a public target should always have an associated 'description' attribute.</li>
+  <li>a private target should begin with a "-"</li>
+</ul>
+
+Those conventions are not mandatory. They just ease understanding.
 
 Example :
 <code type="xml">
-<target name=":helloworld" depends="validate" description="display an hello world">
+<target name="myglugin:helloworld" depends="myplugin:init" description="display an hello world">
     <echo>hello world !</echo>
 </target>
 
-<target name=":hello" depends="validate" depends="-check-username" description="display an hello to current user">
+<target name="myplugin:hello" depends="myplugin:init,-check-username" description="display an hello to current user">
     <echo mess="Hello ${username}"/>
 </target>
 </code>
@@ -128,31 +127,36 @@ Whereas a private target name should beg
 Example :
 <code type="xml">
 <!-- this target initialize username property if it's not already set -->
-<target name="-check-username" unless="username">
+<target name="-myplugin:check-username" unless="username">
     <echo>You can also add a "-Dusername=YOU" on the commandline to display a more personal hello message</echo>
     <property name="username" value="${user.name}"/>
 </target>
 </code>
 
-<h3>The 'doit' Target</h3>
-Each module should have a target called <i>doit</i>. This is an important convention. This target should perform the essential purpose of the module when invoked independently.
-Example: 
+<h3>Pre conditions</h3>
+A build module should always check that a set of pre conditions is met.
+This can be done at the root level of your plugin or in a target. We encourage you to use a target for initialisation as you can control when it should be executed. If intialisation is done at the root level it will be executed when the plugin is loaded.
+
+By convention, the initialisation target should be named ":init".
+
+Pre conditions, including for example - checking the existence of a file or a directory, could be performed inside this target. Additionally, this target is a great place to do global initializations that are needed for the rest of the build. This could include a taskdef initialization.
+Pre conditions can be performed by using <a href="../ref/anttasks/Parametertask.html">parameter task</a>.
+Example :
 <code type="xml">
-<target name="doit" depends=":helloworld"/>
+<target name="myplugin:init">
+    <ea:parameter property="username" required="false" description="the username used to display en 'hello Username' by calling :hello target"/>
+</target>
 </code>
 
-<h3>What a build module should document</h3>
+<h3>What should be documented</h3>
+The following elements needs to be documented
 <ul>
-<li>phases on which it relies</li>
-<li>parameters (properties, resource collections, paths).  For each parameter specify name, description, whether it is required, and optionally a default value.</li>
+<li>public targets / extension points descriptions</li>
+<li>parameters (properties, resource collections, paths).  For each parameter specify name, description, whether it is required, and optionally a default value. This should be done with <a href="../ref/anttasks/Parametertask.html">parameter task</a></li>
 <li>expected environment (files in a directory, a server up and running, ...)</li>
 <li>results produced</li>
 </ul>
 
-A build module should always check that the set of pre conditions is met in the validate phase (for static pre conditions) or at execution (for dynamic pre conditions).
-
-If ever what is considered static pre condition by a module is actually generated by another one, it is still possible to assign the build module validate phase to a phase triggered after the execution of the other build module (using phase mapping with the 'use' task).
-
 <h2>Publishing your plugin</h2>
 You can easily publish your plugin to an easyant repository using the standard phases <i>publish-shared (for snapshot)</i> or <i>release</i>
 <code type="shell">>  easyant publish-local</code>
@@ -175,7 +179,7 @@ Considering that you published your plug
     <info organisation="org.mycompany" module="myproject" 
             status="integration" revision="0.1">
         <ea:build module="build-std-java" revision="0.2">
-            <ea:plugin organisation="org.mycompany" module="myplugin" revision="0.1" as="myplugin"/>
+            <ea:plugin organisation="org.mycompany" module="myplugin" revision="0.1"/>
         </ea:build> 
     </info>
     <publications>
@@ -189,8 +193,8 @@ We should see myplugin's target.
 <code type="shell">
 Main targets:
 ...
- mygplugin:hello                   display an hello to current user
- mygplugin:helloworld              display an hello world
+ myplugin:hello                   display an hello to current user
+ myplugin:helloworld              display an hello world
 ...
 </code>
 
@@ -207,12 +211,12 @@ Open the module.ivy at the root level of
     <info organisation="org.mycompany" module="myplugin" 
             status="integration" revision="0.1">
                 <!-- here we use build-std-ant-plugin build type that provide everything we need for plugin development -->
-        <ea:build module="build-std-ant-plugin" revision="0.1"/>
+        <ea:build module="build-std-ant-plugin" revision="0.9"/>
     </info>
     <configurations>
-        <conf name="default" visibility="public" description="runtime dependencies artifact can be used with this conf"/>
-        <conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases."/>
-        <conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
+        <conf name="default" description="runtime dependencies artifact can be used with this conf"/>
+        <conf name="test" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases."/>
+        <conf name="provided" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
     </configurations>
     <publications>
                 <!--Defines the plugin main script -->
@@ -272,13 +276,13 @@ First we need to declare the dependency 
         <ea:build module="build-std-ant-plugin" revision="0.1"/>
     </info>
     <configurations>
-        <conf name="default" visibility="public" description="runtime dependencies artifact can be used with this conf"/>
-        <conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases."/>
-        <conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
+        <conf name="default" description="runtime dependencies artifact can be used with this conf"/>
+        <conf name="test" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases."/>
+        <conf name="provided" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
     </configurations>
     <publications>
-                <artifact name="myplugin" type="ant"/>
-        </publications>
+        <artifact name="myplugin" type="ant"/>
+    </publications>
 
     <dependencies>
         <!-- your plugin dependencies goes here -->
@@ -301,8 +305,7 @@ org.mycompany#myplugin.classpath
 </code> 
 Since this classpath is auto-created you can use it to reference your taskdef.
 <code type="xml">
-<target name=":init" phase="validate">
-    <ea:parameter phase="validate"/>
+<target name="myplugin:init">
     ...
     <taskdef resource="amazingAntTask.properties" classpathref="org.mycompany#myplugin.classpath" />
     <taskdef resource="anotherAntTask.properties" classpathref="org.mycompany#myplugin.classpath" />
@@ -329,15 +332,10 @@ A task may depend on: 
         <ea:core-version requiredrevision="[0.7,+]" />
 
     <!-- Sample init target -->
-    <target name=":init" phase="validate">
+    <target name=":init">
         <!-- you should  remove this echo message -->
         <echo level="debug">This is the init target of myplugin</echo>
     </target>
-
-    ...
-
-    <!-- define a generic default target for this plugin -->
-    <target name="doit" depends="validate"/>
 </project>
 </code>
 
@@ -346,13 +344,17 @@ By default the skeleton has generated a 
 
 So in our case let's open "src/test/antunit/myplugin-test.xml"
 <code type="xml">
-<project name="org.mycompany;myplugin-test" xmlns:au="antlib:org.apache.ant.antunit">
-    
-    <!-- Mocking required phase --> 
-    <phase name="validate"/>
-        
-    <!-- Import your plugin --> 
-    <import file="../../main/resources/myplugin.ant"/>
+<project name="org.mycompany;myplugin-test" 
+    xmlns:au="antlib:org.apache.ant.antunit"
+    xmlns:ivy="antlib:org.apache.ivy.ant"
+    xmlns:ea="antlib:org.apache.easyant">
+
+    <!-- Import your plugin -->
+    <property name="target" value="target/test-antunit"/>
+    <!-- configure easyant in current project -->
+    <ea:configure-easyant-ivy-instance />
+    <!-- import our local plugin -->
+    <ea:import-test-module moduleIvy="../../../module.ivy" sourceDirectory="../../main/resources"/>
     
     <!-- Defines a setUp / tearDown (before each test) that cleans the environment --> 
     <target name="clean" description="remove stale build artifacts before / after each test">
@@ -366,16 +368,15 @@ So in our case let's open "src/test/antu
     <target name="tearDown" depends="clean"/>
     
     <!-- init test case -->         
-    <target name="testInit">
-        <antcall target=":init"/>
+    <target name="test-myplugin:init" depends="myplugin:init">
         <au:assertLogContains level="debug" text="This is the init target of myplugin"/>
     </target>
     
 </project>   
 </code>
-Considering that our plugin relies on an externally defined phase (validate in our example) we must mock it in our test.
-Then we :
+Here we :
 <ul>
+  <li>configure easyant for test</li>
   <li>import the plugin</li>
   <li>define a generic tearDown, setUp method that cleans the target and lib directories.</li>
   <li>define a test case for the init target that check that the output log contains "This is the init target of myplugin"</li>
@@ -383,10 +384,9 @@ Then we :
 
 All targets prefixed by "test" will be executed as a test case (similar to junit 3 behavior).
 
-Now we will write a test case for our ":helloworld" target.
+Now we will write a test case for our "myplugin:helloworld" target.
 <code type="xml">
-<target name="testHelloWorld">
-    <antcall target=":helloworld"/>
+<target name="test-helloworld" depends="myplugin:helloworld">
     <au:assertLogContains text="hello world !"/>
 </target>
 </code>