ANT

Another Neat Tool

Parte del proyecto Jakarta de Apache .

Ultima versión: Ant 1.7.0 (Diciembre - 2006)

Sistema para controlar y administrar todas las labores de generación de un sistema de software , a partir de archivos fuente (java) y librerías.

Comparación con Make

Make Ant
Dependiente del shell (SO) en donde se ejecuta Independiente del SO pues es una aplicación Java
Las tareas ejecutadas se invocan con la sintaxis del shell Sintaxis Xml para las tareas.
Extendible construyendo aplicaciones que corran dentro del shell (no transportables) Dispone de un Api para incorporar nuevas tareas en java (transportables).
Definición implícita de dependencias entre tareas. Definición explícita de dependencias entre tareas.

En el mundo java, Ant es el estándar de facto para administrar proyectos de software.

Scripts en Ant

Ant es una Aplicación XML en la que se escriben scripts ejecutables.

El procesador de Ant es un intérprete de los scripts.

Los scripts se denominan build files y tienen el nombre (por defecto) build.xml. Cada build file contiene un proyecto.

  <target name="zip_acis">
    <zip zipfile="../CharlaAnt.zip">
       <zipfileset dir="Charlas" prefix='CharlaAnt'>
         <include name="*Ant.html"/>
         <include name="*.css"/>
         
         <include name="Ant/"/>
         <exclude name='Ant/Proyecto/**'/>
         
         <include name="imagenes/buildxml.gif"/>
         <exclude name='**/*.doc'/>
       </zipfileset>
    </zip>
  </target>
  

Un proyecto:

Invocación de Ant

Ant se invoca para ejecutar proyectos:

Targets y dependencias

Un target tiene nombre y su ejecución puede depender de otros targets:

    <target name="A"/>
    <target name="B" depends="A"/>
    <target name="C" depends="B"/>
    <target name="D" depends="C,B,A"/>
Un target se ejecuta una sola vez, aunque varios otros targets dependan de él.

La ejecución de un target puede depender de haber(o no) definido un valor para una propiedad:

<target name="build-modulo-A" if="existe.modulo.a"/>
<target name="build-xref" unless="xref.not.needed"/>

Tareas

Una tarea tiene un nombre, uno o varios atributos (argumentos) y/o elementos anidados.
453:    <java classname='weblogic.ejbc' fork='yes' jvm="${jvm}">
454:      <classpath refid="${extra.ejbc20.classpath}"/>        
455:      <arg value='-idlDirectory'/>
456:      <arg value='${build.tmp.dir}'/>
457:      <arg value='${build.tmp.dir}/pre_${ejb.name}.jar'/>
458:      <arg value='${ejb.dist.dir}/${ejb.name}_ejb.jar'/>
459:    </java>

Existe un conjunto de tareas estándar predefinidas para ant (builtin Tasks).

Entre las más frecuentemente usadas:

Hay un conjunto de tareas opcionales que suelen venir con las distribuciones de ant.

Ejemplos:

El programador puede definir sus propias tareas o puede incorporar tareas escritas por terceros asignándoles un nombre escogido por él.

Tipos de datos

Dentro de un proyecto se pueden definir y referenciar valores de diferentes tipos:

Las definiciones pueden encontrarse por fuera de los targets o pueden ser elementos internos de los targets.

Propiedades

Análogo de las variables. Todo proyecto maneja un conjunto de propiedades cuyos valores:

El valor de una propiedad es una cadena de caracteres que se puede reemplazar dinámicamente durante la ejecución de un script mediante la notación ${Nombre-de-la-propiedad}

   1:<project name="MyProject" default="dist" basedir=".">
   2:
   3:  <!-- set global properties for this build -->
   4:  <property name="src" value="."/>
   5:  <property name="build" value="build"/>
   6:  <property name="dist"  value="dist"/>
   7:
   8:  <target name="init">
   9:    <!-- Create the time stamp -->
  10:    <tstamp/>
  11:    <!-- Create the build directory structure used by compile -->
  12:    <mkdir dir="${build}"/>
  13:  </target>
  14:
  15:  <target name="compile" depends="init">
  16:    <!-- Compile the java code from ${src} into ${build} -->
  17:    <javac srcdir="${src}" destdir="${build}"/>
  18:  </target>
  19:
  20:  <target name="dist" depends="compile">
  21:    <!-- Create the distribution directory -->
  22:    <mkdir dir="${dist}/lib"/>
  23:
  24:    <!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->
  25:    <jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
  26:  </target>
  27:
  28:  <target name="clean">
  29:    <!-- Delete the ${build} and ${dist} directory trees -->
  30:    <delete dir="${build}"/>
  31:    <delete dir="${dist}"/>
  32:  </target>
  33:</project>

Salvo excepciones, una vez que una propiedad toma un valor éste no puede ser cambiado.

Paths

Un Path o un Path Structure es una lista ordenada de caminos en el sistema de archivos.

2:   <classpath>
3:     <pathelement location="lib/libreria.jar"/>
4:   </classpath>

Se puede poner un nombre (id) a un path para ser referenciado posteriormente:
33:   <path id="weblogic.classpath">
34:      <pathelement location="${weblogic.jar}"/>
35:      <pathelement location="${weblogic_sp.jar}"/>
36:   </path>

Se puede "extender" un path referenciando a otro.

42:   <path id="deployment.classpath">
43:      <pathelement location="${javatools.jar}"/>
44:      <path refid="weblogic.classpath"/>
45:    </path>
46:

Se usan en compilaciones, ejecución de clases java:

     <javac destdir="${build.classes.dir}"
            srcdir="${src.dir}">
       <classpath refid="weblogic.classpath"/>
       <include name="**/*.java"/>
     </javac>

     <javac destdir="${build.classes.dir}"
            srcdir="${src.dir}">
        <classpath>
          <pathelement location="lib/libreria.jar"/>
        </classpath>
        <include name="**/*.java"/>
     </javac>
    <java classname='weblogic.ejbc' fork='yes'>
      <classpath refid="deployment.classpath"/>        
      <arg value='-idlDirectory'/>
      <arg value='${build.tmp.dir}'/>
      <arg value='${build.tmp.dir}/pre_${ejb.name}.jar'/>
      <arg value='${ejb.dist.dir}/${ejb.name}_ejb.jar'/>
    </java>

Filesets

Un Fileset es un conjunto de archivos que tienen un directorio común: Casi todas las tareas pueden utilizar filesets:
      <copy todir="new-web">
        <fileset dir="web">
           <include name="**/*.html"/>
           <exclude name="**/*.css"/>
        </fileset>
      </copy>

Patternsets

Un Patternset es un conjunto de patrones que coinciden con archivos. Un patternset tiene que hacer parte de un fileset para referirse a archivos específicos.

Reglas de los patrones:
Patrón Coincidencia
* Cero o más caracteres.
? Un caracter.
** Usado como nombre de directorio, coincide con todos los directorios desde ese punto en adelante.
Terminando en / Equivale a terminar con **

Los patternsets de un fileset se pueden especificar mediante atributos:

<fileset dir="web" includes="**/*.html" excludes="**/*.css"/>

Los patternsets se pueden anidar y referenciar:

   <patternset id="image.files" includes="**/*.gif,**.jpg"/>

   <patternset id="binary.files">
     <exclude name="**/*.txt"/>
     <exclude name="**/*.xml"/>
     <patternset refid="image.files"/>
   </patternset>

      <copy todir="${img.dir}">
        <fileset dir="web">
           <patternset refid="binary.files"/>
        </fileset>
      </copy>

Filterset

Un Filterset es un conjunto de filtros. Los Filtros son especificaciones de sustituciones sencillas de texto.

Un texto base para modificar:

<html>
  <head><title>Ant</title></head>
  <body>
    El tiempo ahora: @FECHA@ @ @HORA@
  </body>
</html>

Un target que produce una copia modificada:

   <target name="filtrar">
      <tstamp/>
      <copy todir="new-web" overwrite="true">
        <fileset dir="web" includes="**/*.html"/>
        <filterset>
          <filter token="FECHA" value="${DSTAMP}"/>
          <filter token="HORA" value="${TSTAMP}"/>
        </filterset>
      </copy>
   </target>

El resultado podría ser:
<html>
  <head><title>Ant</title></head>
  <body>
    El tiempo ahora: 2007/05/18 @ 21:00
  </body>
</html>

Mappers

Permiten definir correspondencias entre conjuntos de archivos.
Tipos de Mappers
Tipo Descripción
identidad El nombre del destino es igual al de la fuente.
flatten Los nombres son iguales pero los destinos pierden su "path prefix".
merge Todos los fuentes corresponden a un archivo destino que se especifica en el atributo to.
glob Los nombres que coincidan con un asterisco usado en el patrón from reemplazan a los nombres que coincidan con el asterisco del patrón to. Solamente se consideran los nombres que coincidan con el patrón from.
package Como el tipo glob pero los separadores de paths se reemplazan por punto (".")
regexp los patrones from y to se definen mediante expresiones regulares.

Mapper identidad

    <copy todir="new_web">
      <fileset dir="web" includes="**/*.jsp"/>
      <mapper type="identity"/>
    </copy>

tiene el mismo efecto que:

    <copy todir="new_web">
      <fileset dir="web" includes="**/*.jsp"/>
    </copy>

Mapper flatten

    <copy todir="new_web">
      <fileset dir="web" includes="**/*.jsp"/>
      <mapper type="flatten"/>
    </copy>

Copia una jerarquía de directorios en un directorio "plano".

Mapper merger

Todos los fuentes corresponden a un solo destino. Es útil con la tarea uptodate.

    <uptodate property="zip.not.required">
      <srcfiles dir="src" includes="**/*.java"/>
      <mapper type="merge" to="${dist.dir}/src.zip"/>
    </uptodate>

Mapper glob

Los patrones definidos en los atributos from y to deben contener exactamente un asterisco.

Es útil para hacer copias de respaldo combiando los nombres.

    <copy todir="new_web">
      <fileset dir="web" includes="**/*.jsp"/>
      <mapper type="glob" from="*.jsp" to="*.jsp.bak"/>
    </copy>

Un ejemplo

Manejo de un proyecto J2EE

Dependencias entre build files

Administración de pruebas unitarias

Las facilidades ofrecidas por el ambiente de pruebas JUnit pueden utilizarse desde Ant, con algunas ventajas:

Clases de prueba en JUnit

Pasos para crear una clase de prueba:
  1. Crear una subclase de junit.framework.TestCase .
  2. Escribir un constructor con exactamente un parámetro String nombre y que invoca super(nombre).
  3. Implementar uno o más métodos, sin argumentos, cuyos nombres comiencen con la palabra test.
import junit.framework.TestCase;

public class SimpleTest extends TestCase {
  public SimpleTest (String nombre){
    super(nombre);
  }
  
  public void testAlgo(){
    assertTrue (5 == (2+3));
  }
}
Ejecución de una prueba:

java junit.textui.TestRunner SimpleTest

java junit.swingui.TestRunner SimpleTest

JUnit verifica el éxito o falla de una prueba mediante aserciones:

assertEquals(valorEsperado, valorReal);
assertEquals(mensaje,valorEsperado, valorReal);

assertNull(objecto);
assertNull(mensaje,objeto);

assertNotNull(objeto);
assertNotNull(mensaje,objeto);

assertSame(valorEsperado, valorReal);
assertSame(mensaje,valorEsperado, valorReal);

assertTrue(valorBooleano);
assertTrue(mensaje,valorBooleano);


Se puede forzar una falla en una prueba mediante:

fail();
fail(mensaje);


Terminología JUnit:

Ciclo de vida de un TestCase

  1. Ejecutar public void setUp().
  2. Llamar a un método cuyo nombre comience por test.
  3. Ejecutar public void tearDown().
  4. Repetir los tres primeros pasos para cada método de "test".
  5. Los métodos setUp y tearDown se ejecutan antes y después de la ejecución de cada método de test!!

Suites de tests

Las pruebas se pueden agrupar en suites. Una suite puede contener pruebas aisladas y suites de pruebas.
public class TodasLasPruebas extends TestSuite{
  static public Test suite(){
    TestSuite suite = new TestSuite();
    suite.addTestSuite(SimpleTest.class);
    return suite;
  }
}
En Ant no hay que crear suites. El elemento batchtest permite definir el conjunto de pruebas que se quieren ejecutar.

Organización del código para pruebas

  <!-- Classpath para las pruebas -->
  <path id="test.classpath">
    <path refid = "compile.classpath"/>
    <pathelement location="${junit.jar}"/>
    <pathelement location="${build.dir}/classes"/>
    <pathelement location="${build.dir}/test"/>
  </path>
  
  <target name="init">
    <!-- Inicializaciones globales del proyecto -->
  </target>
  
  <target name="compile" depends="init">
   <!-- Compilación de los fuentes del sistema (sin las pruebas). -->
  </target>
  
  <target name="test_init" depends="init">
   <!-- Inicializaciones para las pruebas. -->
     <mkdir dir="${test.dir}"/>
     <delete dir="${test.data.dir}"/>
     <delete dir="${test.reports.dir}"/>
     <mkdir dir="${test.data.dir}"/>
     <mkdir dir="${test.reports.dir}"/>
  </target>
  
  <!-- Compilación de las pruebas -->
  <target name="test-compile" depends="compile,test_init">
     <javac destdir="${test.dir}" srcdir="${test.src}">
       <classpath refid="test.classpath"/>
     </javac>
  </target>
Una clase de prueba

Ejecución de las pruebas desde Ant

  <!--  Ejecuta una prueba -->
  <junit printsummary="false"  haltonfailure="true">
    <classpath refid="test.classpath"/>
    <formatter type="xml"/>
    <test todir="${test.data.dir}"
          name="org.acis.UnTest"/>
  </junit>
  <!--  Ejecuta un conjunto de pruebas -->
  <junit printsummary="false"  haltonfailure="true">
    <classpath refid="test.classpath"/>
    <formatter type="xml"/>
    <batchtest todir="${test.data.dir}">
      <fileset dir="${test.dir}" includes="**/*Test.class"/>
    </batchtest>
  </junit>

Generación de reportes de pruebas

A partir de resultados formateados en xml se pueden generar reportes HTML al estilo javadoc.
  <target name="test" depends="test-compile">
    <!-- Ejecuta un conjunto de pruebas -->
    <junit printsummary="false"  haltonfailure="true">
      <classpath refid="test.classpath"/>
      <formatter type="xml"/>
      <batchtest todir="${test.data.dir}">
        <fileset dir="${test.dir}" includes="**/*Test.class"/>
      </batchtest>
    </junit>
    
    <!-- genera reportes en HTML -->
    <junitreport todir="${test.data.dir}">
      <fileset dir="${test.data.dir}">
        <include name="TEST-*.xml"/>
      </fileset>
      <report format="frames"
         todir="${test.reports.dir}"/>
    </junitreport>
  </target>

Extendiendo a Ant

Ant ofrece un api de programación que permite incorporar nuevas tareas.
   1:  <target name="init">
   2:    <taskdef name="dropsection" 
   3:             classname="com.cincosoft.ant.Verify5SoftTask" 
   4:             classpath="${tasks.path}"/>
   5:  </target>
   6:
   7:   <target name="drop-section" depends="init">
   8:    <dropsection>
   9:      <fileset dir="${drop-section.dir}">
  10:        <exclude name="**/*.gif"/>
  11:        <exclude name="**/*.jpg"/>
  12:        <exclude name="**/*.jpeg"/>
  13:      </fileset>
  14:    </dropsection>
  15:   </target>
  16:  
Fuentes java