Mittwoch, 11. März 2009

 

CRISP, ANT und JAR

Zubereitung von CRISP Builds

Ein Build ist eine Reihe von Schritten, die Ihre kreativen Artefakte (Source Code) in auslieferbare Software transformieren.

CRISP bedeutet

vollständig (engl. Complete)
wiederholbar (engl. Repeatable)
informativ (engl. Informative)
planbar (engl. Schedulable)
portabel (engl. Portable)

Ein CRISP Build versetzt uns in die Lage jederzeit automatisch den Quellcode in auslieferbare Software zu transferieren.

Complete - vollständig heißt, dass alles von Grund auf nach dem Rezept neu erstellt wird. Es braucht nichts vor oder nach dem Build hinzugefügt werden.
Repeatable wiederholbar heißt, dass jederzeit auch frühere Softwareversionen erzeugt werden können. Der Schlüssel dazu liegt in der Versionsverwaltung (CVS).
Informative - informativ bedeutet, dass wir sofort wissen, wenn der Build schief gegangen ist. Der Build war erfolgreich, wenn die Software fehlerfrei übersetzt werden konnte und die automatischen Test bestanden hat.
Schedulable - planbar oder zeitgesteuert - ist ein Build vollständig und wiederholbar, so kann er auch automatisch z.B. zu bestimmten Zeiten angestoßen werden. Der Vorteil ist, dass man nebenher am Quellcode weiterarbeiten kann.
Portable - portable Builds bedeuten weniger, dass Builds, die auf einem Unix durchgeführt werden, auch unter Windows durchgeführt werden können. Jedoch soll der Build ohne Probleme auf einem andern Unix-System laufen. Abhängigkeiten von bestimmten IP-Adressen oder IDE sind zu vermeiden.

Der "Compile"-Knopf ist kein Build Prozess

So verlockend es auch sein mag, ihn als solchen zu benutzen, der "Compile"-Knopf ist kein Build-Prozess, der einen CRISP-Build erzeugt. Sie müssten sich auf eine einzige IDE mit einer Standardkonfiguration einigen (Einigung durch Armdrücken? - was ist die beste IDE?). Das erfordert Disziplin, denn jede Änderung der Einstellung in der IDE müsste an alle verteilt werden (CVS).

Wie automatisiert man den Build-Prozess nun mit der IDE? Ein Programmierer drückt auf den Knopf, wenn ihm eine Banane (oder ein Bier :-) hingehalten wird?

Mehr Flexibilität erhält man, durch die Verwendung eines aus jeder IDE ausgelagerten Build-Prozesses.

ANT

another neat tool - ANT ist ein auf Java-Projekte spezialisiertes Open-Source-Build-Werkzeug, welches XML-Dateien zur Steuerung verwendet. Einige IDEs können ANT-Dateien erzeugen bzw. ANT für den Build-Prozess verwenden ("Compile"-Knopf mit ANT im Hintergrund). ANT versetzt uns in die Lage CRISP-Builds zu machen.

Vorteile

- ANT-Dateien sind portabel und da ANT in Java geschrieben wurde, ist auch der Build-Prozess portabel.
- ANT verfolgt Abhängigkeiten (ähnlich make). Es ruft javac nur auf, wenn sich eine Datei geändert hat (das macht make auch, aber es bei Java effizienter, da alles in einer JVM läuft, bei make würde für jden javac -Aufruf eine JVM gestartet werden müssen).
- ANT beinhaltet neben dem Kompilieren noch eine Reihe von Tasks zur Erledigung verschiedener Dinge (z.B. JUnit -Tests). Außerdem können eigene in Java geschriebene Tasks verwendet werden.

Nachteile

Das Build-Rezept muss in einer XML-Datei ausgedrückt werden. All die eckigen XML-Klammern können verschleiern, was wirklich vor sich geht.

Eine ANT-Build Datei build.xml


<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="build" name="AntDemo">
<property name="JUNIT_HOME" value="vendor">
<property name="build.dir" location="build">
<property name="doc.dir" location="doc">
<property name="src.dir" location="src">
<property name="vendor.dir" location="vendor">

<path id="project.classpath">
<pathelement location="bin">
<pathelement location="build">
<pathelement location="${JUNIT_HOME}/junit.jar">
</path>

<!-- Initialisierungen -->
<target name="init">
<mkdir dir="bin">
<mkdir dir="build">
</target>

<!-- löschen der binaries -->
<target name="clean">
<delete dir="bin">
<delete dir="build">
</target>

<!-- Übersetzen -->
<target depends="init" name="compile" description="Übersetzen">
<javac destdir="bin">
<src path="src">
<classpath refid="project.classpath">
</javac>
</target>

<!-- Unit Tests, vorher evtl. Übersetzen -->
<target depends="init,compile" name="test" description="Testen">
<junit fork="yes" printsummary="withOutAndErr">
<formatter type="plain">
<test name="antdemo.DatumTest">
<classpath refid="project.classpath">
</junit>
</target>

<!-- erzeuge jar-File, vorher evtl. übersetzen, Test-Klassen werden nicht
ausgeliefert -->
<target depends="init,compile" name="build" description="Build jar">
<jar destfile="build/antdemo.jar" basedir="bin" excludes="**/*Test.class">
<manifest>
<!-- erzeuge Minifest ... -->
<attribute name="Main-Class" value="antdemo.Datum">
</manifest>
</jar>
</target>
</project>

junit.jar (die für die Unit-Tests nötige Bibliothek) ist in diesem Beispiel im Verzeichns vendor zu finden. Die jar-Datei wird im Verzeichnis build erstellt. Die übersetzten Klassendateien sind im Verzeichnis bin.

Aufruf: ant [task]

Wird kein task angegeben, so wird der Default-Task ausgeführt (im Projekt definiert: <project basedir="." default="build" name="AntDemo">). Abhängigkeiten werden durch depends= definiert.

Erzeugen von jar-Dateien und einbinden fremder jar-Dateien

Oft benötigt man weitere jar Dateien. Diese müssten beim ausgelieferten Programm entweder im Class-Path sein oder man packt die jar Datei aus und im Ziel- jar wieder ein.


<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="build" name="AntDemo">
<property name="JUNIT_HOME" value="vendor">
<property name="build.dir" location="build">
<property name="doc.dir" location="doc">
<property name="src.dir" location="src">
<property name="vendor.dir" location="vendor">

<path id="project.classpath">
<pathelement location="bin">
<pathelement location="build">
<pathelement location="${JUNIT_HOME}/junit.jar">
</path>

<!-- Initialisierungen -->
<target name="init">
<mkdir dir="bin">
<mkdir dir="build">
</target>

<!-- löschen der binaries -->
<target name="clean">
<delete dir="bin">
<delete dir="build">
</target>

<!-- Übersetzen -->
<target depends="init" name="compile" description="Übersetzen">
<javac destdir="bin">
<src path="src">
<classpath refid="project.classpath">
</javac>
</target>

<!-- Unit Tests, vorher evtl. Übersetzen -->
<target depends="init,compile" name="test" description="Testen">
<java fork="yes" classname="antdemo.DatumTest">
<classpath refid="project.classpath">
</java>
<!--
<junit fork="yes" printsummary="withOutAndErr">
<formatter type="plain">
<test name="antdemo.DatumTest">
<classpath refid="project.classpath">
</junit>
-->
</target>

<!-- erzeuge jar-File, vorher evtl. übersetzen, Test-Klassen werden nicht
ausgeliefert -->
<target depends="init,compile" name="build" description="Build jar">
<jar destfile="build/antdemo.jar" basedir="bin" excludes="**/*Test.class">
<manifest>
<!-- erzeuge Manifest ... -->
<attribute name="Main-Class" value="antdemo.Datum">
</manifest>
</jar>
</target>

<!-- erzeuge jar-File incl. junit, vorher evtl. übersetzen, Test-Klassen werden nicht
ausgeliefert
junit.jar wird zuerst ins bin ausgepackt und dann ins jar wieder
eingepackt -->
<target depends="init,compile" name="testbuild" description="Build jar">
<unjar src="vendor/junit.jar" dest="bin">
<jar destfile="build/antdemo.jar" basedir="bin">
<manifest>
<!-- erzeuge Manifest ... -->
<attribute name="Main-Class" value="antdemo.DatumTest">
</manifest>
</jar>
</target>
</project>

Das Ziel testbuild (das letzte) enthält zuerst einen Task , der junit.jar auspackt. Bei diesem Beispiel wird eingenommen, dass im Projektverzeichnis ein Unterverzeichnis vendor existiert, in dem eine Kopie von junit.jar zu finden ist.

junit.jar wird nach bin ausgepackt (dort landen alle *.class Dateien) und dann gemeinsam mit den eigenen Klassen zu build/antdemo.jar verpackt. <manifest...> erstellt das nötige Manifest, um die Datei mit java -jar antdemo.jar aufrufen zu können.

Beim Ziel build (das vorletzte) werden die Testklassen exkludiert und eine andere Startklasse ins Manifest eingetragen.

Das gesamte Beispielprojekt finden Sie hier

Die Verzeichnisstruktur ist an Projektstruktur angelehnt.

Weitere Informationen

ant.apache.org
Wikipedia Ant
projektstruktur.zip

Labels: ,


Kommentare:

Kommentar veröffentlichen

Abonnieren Kommentare zum Post [Atom]





<< Startseite

This page is powered by Blogger. Isn't yours?

Abonnieren Posts [Atom]