Export using ANT

One big advantage of the Processing development environment is the seamless export of the sketch in a web applet or even application for the Windows, OS X or Linux distribution. Right now the only way to execute the program is to press the “run” button in the tool bar of eclipse. Always starting eclipse just for presenting your work isn't just a hassle – it also wreaks performance by monitoring the sketch with the debugger. We are going to use Ant (Another Neat Tool) to convert the source files into executable jars or applications.

About ANT

Apache Ant is developed by the Apache Software Foundation and available as open-source product since the year 2000. It's based on Java and focuses on the creation and management of mainly Java Projects, like Maven for C++. Because of the Java core it's a cross-platform tool (except some platform specific commands); the scripts are moveable from one operating system to another unchanged. For the founder James Duncan Davidson the acronym stands for the insect which can overcome incredible tasks using little size and power. Truly the potential of ANT reaches from simple filesystem operations like creating/deleting folders and files up to getting source documents from a svn repository and combining them with local documents or sending mails with reports or documentations to the rest of the team. For an even wider range extension can be installed or self made Java classes implemented.

Language

Structure

Each project starts with a simple text document called “build.xml”. Written in the XML standard the process is structured hierarchical and can be separated in different tasks (target) or tasks chains by defining dependencies. If a target node has an depends attribute ANT looks up the specific target and performs it before the actual target is executed.

<?xml version="1.0"?>
<project name="Go out">
	<target name="shut down pc">
	</target>
 
	<target name="choose club" depends="shut down pc">
	</target>
 
	<target name="dress" depends="choose club">
	</target>
 
	<target name="leave" depends="dress">
	</target>
</project>

In the root node (project) of the XML tree an optional default task/target can be defined as main process for the whole build file.

<?xml version="1.0"?>
<project name="Go out" default="leave">

Another type of node that ANT understands is called property. Allowing you to set a container with a custom value in the initial phase of the script and accessible with the ${“name”} syntax within the target nodes. One big disadvantage of the default ANT application is that those values are not changeable during the process. Some plugins solve the problem, but sometimes you aren't able to suppose that every developer is running the same environment configurations like you do.

<?xml version="1.0"?>
<project name="Go out" default="leave">
	<property name="me" value="Lucky Luke" />
 
	<target name="shut down pc">
		<echo>${me} shuts down the pc.</echo>
	</target>

A bunch of methods offer the dynamic definition of property elements and enable a low level conditional approach. Properties that have been defined in the markup like me and the dynamic versions can be questioned in the target node using the attributes if and unless. Crucial for the condition result is the existence of the property, not the value.

Here is a selection of three (complete list):

  • available Sets a property if a specified file, directory, class in the classpath, or JVM system resource is available at runtime.
  • loadfile Loads a file into a property.
  • pathconvert Converts a path reference, filelist or fileset to a list of items separated by a specified separator.

In the example case below the property flag.cleanshirt is defined before the first target started and represents the value false. During the shut down pc action the result of a file existence check for a text document named todo.txt (in the folder where our build.xml is located) influences the determination of the property called flag.todo. If there is no such file - the property won't be defined by ANT. Target number two is verifying the property flag.todo with the unless condition and only will be invoked if the property hasn't been defined. Otherwise the whole chain of targets will be stopped at this point. Presume there is no file that keeps us from going out. Target dress would be called and the node-attribute if implies that flag.cleanshirt must be available - the actual value of the property is unimportant for the execution.

<?xml version="1.0"?>
<project name="Go out" default="leave">
	<property name="me" value="Lucky Luke" />
	<property name="flag.cleanshirt" value="false" />
 
	<target name="shut down pc">
		<available property="flag.todo" file="todo.txt" />
	</target>
 
	<target name="choose club" depends="shut down pc" unless="flag.todo">
		<echo>${me} has nothing to do.</echo>
	</target>
 
	<target name="dress" depends="choose club" if="flag.cleanshirt">
		<echo>${me} is ready to leave</echo>
	</target>

To solve the problem we have to use two more basic tasks: condition and matches. Number one allows us to define a property while the build script is running, depending on the result of nested tasks. In this case the nested node matches checks if the value of the by the user defined property is true.

<?xml version="1.0"?>
<project name="Go out" default="leave">
	<property name="me" value="Lucky Luke" />
	<property name="flag.cleanshirt" value="false" />
 
	<target name="shut down pc">
		<available property="flag.todo" file="todo.txt" />
	</target>
 
	<target name="choose club" depends="shut down pc" unless="flag.todo">
		<echo>${me} has nothing to do.</echo>
		<condition property="flag.reallycleanshirt">
			<matches string="${flag.cleanshirt}" pattern="true" />
		</condition>
	</target>
 
	<target name="dress" depends="choose club" if="flag.reallycleanshirt">
		<echo>${me} is ready to leave</echo>
	</target>

File system

Create

Create a new text document:

<echo file="document.txt">Thats the content of the new file.</echo>

Create a new folder:

<mkdir dir="Folder name" />
Delete

Delete a single file or a folder:

<delete file="lib/log.txt" />
<delete dir="Temp" />

Delete all txt files in the folder named Temp:

<delete>
	<fileset dir="Temp">
		<filename name="*.txt" />
	</fileset>
</delete>

Delete all txt files in the folder named Temp including all sub directories:

<delete>
	<fileset dir="Temp">
		<filename name="**/*.txt" />
	</fileset>
</delete>
Select

Whole folder content:

<fileset dir="Temp" />

Folder and sub folder content except files that contain “Private”:

<fileset dir="Temp">
	<exclude name="**/*Private*.*" />
</fileset>

Only .java source files without the compiled .class versions:

<fileset dir="Temp">
	<filename name="**/*.java" />
	<not>
		<filename name="**/*.class" />
	</not>
</fileset>
Read

Loads a file content into a property:

<loadfile property="content" srcFile="readme.txt" />
<echo>${content}</echo>
Move / Rename

Renames a file from source.txt to target.txt.

<move file="source.txt" tofile="target.txt" />

Moves files from one into another directory:

<move todir="New folder">
	<fileset dir="Orig Folder" />
</move>

Java

Create a jar bundle containing the source folder of the project:

<jar destfile="library.jar">
	<include name="src" />
</jar>

Create a jar bundle of the class folder containing a manifest:

<jar destfile="application.jar">
	<include name="class" />
	<manifest>
		<attribute name="Main-Class" value="com.mycompany.myproject.Main" />
		<attribute name="Build-By" value="Lucky Luke" />
	</manifest>
</jar>

Unbundle a jar file:

<unjar src="library.jar" dest="lib" />

Compile Java source code:

<javac srcdir="class" destdir="build" optimize="true" />

Run jar document:

<java jar="application.jar" failonerror="true" />

Example scripts

Nine tiny example scripts (right click, save as):

1. Hi!
2. Modularity
3. Properties
4. Conditions
5. File system 1
6. File system 2
7. Zip
8. Environment Variables
9. Loading/editing text

eclipse implementation

Ant window

eclipse ANT window As part of the core plugin package the Ant implementation is shipped with every eclipse classic application. In the default workspace it shouldn't appear – so we have to add the window which allows to manage the build file. Therefore select WindowShow ViewAnt in the eclipse toolbar and a tiny window (like displayed in the image on the right) should show up somewhere. In my layout it's always positioned at the lower left corner of the eclipse window.

The difference between the image on the right and your window is the content that isn't displayed jet in your version. To add a build file in the manager simply press the button with the +ant icon or drag the document directly into the frame. The small arrow on the left let's you expand the document. From project to target to the last child in the XML structure of your build file. That makes the Ant window some kind of Outliner, which we know from the Java development in eclipse. To remove a build file from the list simply select the root knot (project) and press the grey x in the bar located at the top of the window frame.
Named is the project in the list after the name attribute in the project knot. Select a name that describes the process which will executed and use prefixes if you have multiple files within one project. For example project-deploy, project-backup and so on.

Last important part is how to launch the so called targets. Using the play button in the toolbar or by double clicking each single target can by executed (including it's dependencies). If you defined a default target for the whole build file in the project knot – the project item is also “playable”. In the screen shot on the right the default target is marked with the blue triangle.

Processing script

The following Ant build file is a basic approach for exporting Processing based Java sketches. Currently only tested on OS X (Processing 1.0.7). For advices, comments and bugs please contact me (ste.fiedler[near]gmail.com).

http://creativecoding.org/examples/wiki/p5/p5-ant-export.dmg http://creativecoding.org/examples/wiki/p5/p5-ant-export.zip

Features

  • Web export
  • OS X .app export (including optional .dmg packaging)
  • OpenGL and P2D handling
  • full project backup as .zip file

Example Projects (Processing 1.0.7 )

Usage

Installation

Package Explorer

  1. If you want to run the script on Mac OS X download the .dmg file. One important file for the .app creation will otherwise be corrupted. All other OS users will only be able to use the web export and can download the .zip version.
  2. Extract the archive or disk image and copy the ant folder into your project root directory of the eclipse project. Refresh the package explorer in eclipse and the folder will appear. You also can simply drag the ant directory into the package explorer and eclipse will place a copy in your project folder. Everything including the build file, html template, OS X application stuff is located directly or in of the subfolders of the ant directory.
  3. Now select the build.xml and drag it into the eclipse Ant Window. Open the build.xml and edit the name attribute of the project knot (your project name for ie.). This definition will be used as title in the Ant window and can save you lots of troubles when working on several projects at the same time.
  4. Edit the following property definitions in the build.xml:
    • main.class: The name of the class that extends PApplet. Without the .java document type extension.
    • main.package.name: The location of the main.class in the src folder. For example if the class is based in src/com/company/project/ it has to look like com.company.project. – has to end with a . (dot). Leave it blank if your main class is located in the src folder.
    • main.package.path: Like the main.package.name. Just replace each dot with a / (slash).
    • applet.width (web export only)
    • applet.height (web export only)
  5. Make sure your project folder contains all those folders, even if they are empty:
    project folder
     |
     + ant
        |
        + mac
        + web
        - build.xml
     + data
     + lib
     + libNative
     + src
    
  6. Last character in your main class must be a }. Make sure there is no space, tab or newline behind it. If someone knows the the regex for it – please contact (ste.fiedler[near]gmail.com).

Building

Each numeric indexed target is designed for executing a specific export task. All other targets like compile, osx.dmg and so one are called by one of the five main targets and won't work if they are run on their own.

  • 1. clean removes all directories that are left from an export run. Normally each target deletes all temporary created directories which have been created. If an error occurred – the target hasn't been finished in the way it should – some corps can be left in your project folder. Use this target to clean it.
  • 2. clean deploy removes the applet folder, .app application and all backup zip files from the project folder.
  • 3. deploy OSX creates an OS X application from scratch. The icon is located in ant/mac/application.icns.
  • 4. deploy WEB Processing IDE like web export as html document with implemented applet. Mind that the JVM settings xmx and xms for the heap space won't work (security stuff).
  • 5. deploy ZIP stuffs a zip archive including the folders ant, data, lib, libNative and src. A text file containing some data/project/settings informations will be also placed in the root tree.

Script

<?xml version="1.0"?>
<!--
	topic:		PROCESSING(.ORG) EXPORT FROM ECLIPSE
	date:		06-10-2008
	author:		Steffen Fiedler // ste.fielder[near]gmail.com
 
	==================== Conditions that have to be provided =======================
 
	* Your project folder has to look like this:
 
	Project folder
	|
	+.. ant
	|	 |
	| 	 +.. mac	Documents for OS X .app creation
	| 	 +.. web	Documents for web applet creation
	|	 - build.xml	ANT script
	|
	+.. src			Java source code
	+.. data		Data files (fonts, images, etc.)
	+.. lib			External Java libraries
	+.. libNative		External Java native libraries	
 
	* Keep in mind that all the parts which normally the preprocessor of the processing 
	IDE does, are now on your todo list. You have to write pure Java - with the use of 
	the processing frame work.
 
	==================== Useage of the predefined targets ===========================
	* Only run the from 1 to 5 indexed targets. All other targets in this script 
	are invoked by one of the five.
 
	* All files in the ant/web directory will be copied to the applet folder when 
	running the "4. deploy WEB" target. Extend or modifie the html templates and 
	save them with the original name - don't touch the applet embed part.
 
	* OpenGL used/not used decision is made by looking up the processing.opengl 
	package import in the head of the main.class document.
 
	* The XCode developer tools are not necessarily needed to create a Mac OS X 
	application bundle. Just remove the comment if you have them installed to 
	make sure that it really works - like Apple want's you to do.
 
	* Have a look at the Apple Java Runtime System Properties for an overview 
	of all settings of .app documents:
 	http://beta.devworld.apple.com/documentation/Java/Reference/Java14SysProperties/
 
	==================== Properties to modify =======================================
	•••• PROJECT SETTINGS
	project.author			Name, email or both of the project author/
					creator. Will be included in the jar-manifest, 
					applet html-document and OS X app description.
 
	project.author.appleId		Apple developer identification number 
					of the project author. Default value 
					for "unknown" is "????".
 
	project.name			Displayed in the web html-document and as title 
					of the OS X application in the menu bar.
 
	main.class			Name of the project main class which extends PApplet.
					Without the .java file type ending.
 
	main.package.name		Name of the package where the main class is located. 
					This string has to end with a dot (.). Leave it blank 
					if the main class is in the default src folder.
 
	main.package.path		Same as "main.package.name" - just replace each . with 
					a fraction / .
 
	•••• WEB APPLET SETTINGS
	applet.width /			The catual size of your applet canvas.
	applet.height
 
	•••• OS X APP SETTINGS
	jvm.version			Required jre version.
	jvm.xms				Initial Java heap size.
	jvm.xmx				Maximum Java heap size.
 
	osx.brushmetal			Turns on/off Aqua look of the application 
					window frame.
 
	osx.devtools			Location of the XCode Developer tools on your 
					harddrive. Script is using SetFile to convert 
					the folder into an .app bundle.
 
	osx.dmgcopy			If "true" ANT creates a disk image which contains 
					a copy of the generated OS X project application. Leaf 
					blank for no creation after running "deploy OSX".
 
	osx.presentmode			Initial user-interface mode for menu bar and dock of
					Mac OS X. Values from 0 to 4 accepted. For more information 
					look up: http://tinyurl.com/3rcoqo - or look up the web 
					for "LSUIPresentationMode".
 
	osx.safeposition		Enforces the application window to get not out 
					of the desktop canvas. For more information google 
					"apple.awt.window.position.forceSafeUserPositioning".
-->
<project name="Processing export" default="4. deploy WEB" basedir="../">
 
	<!-- ###### START EDIT ###### 
	Edit the following properties if necessary
	-->
 
	<!-- Project settings -->
	<property name="project.author" value="Lucky Luke" />
	<property name="project.author.appleId" value="????" />
	<property name="project.name" value="My Project" />
	<property name="project.version" value="1.0" />
 
	<property name="main.class" value="Main" />
	<property name="main.package.name" value="com.mycompany.myproject." />
	<property name="main.package.path" value="com/mycompany/myproject/" />
 
	<!-- Web applet settings -->
	<property name="applet.width" value="500" />
	<property name="applet.height" value="500" />
 
	<!-- OS X app settings -->
	<property name="jvm.version" value="1.4+" />
	<property name="jvm.xms" value="128" />
	<property name="jvm.xmx" value="256" />
 
	<property name="osx.brushmetal"	value="false" />
	<property name="osx.devtools" value="/Developer" />
	<property name="osx.dmgcopy" value="false" />
	<property name="osx.presentmode" value="0" />
	<property name="osx.safeposition" value="true" />
 
	<!-- ###### END EDIT ###### -->
 
	<!-- Project related folders -->
	<property name="dir.data" value="data" />
	<property name="dir.lib" value="lib" />
	<property name="dir.libNat" value="libNative" />
	<property name="dir.src" value="src" />
 
	<!-- ANT procedure folders -->
	<property name="dir.build" value="build" />
	<property name="dir.class" value="class" />
	<property name="dir.applet" value="applet" />
 
 
	<!-- CLEAN WORKSPACE -->
	<target name="1. clean">
		<delete dir="${dir.build}" />
		<delete dir="${dir.class}" />
		<delete dir="${dir.doc}" />
		<delete dir="temp" />
	</target>
 
	<!-- REMOVES DEPLOYED -->
	<target name="2. clean deploy">
		<delete dir="${dir.applet}" />
		<delete dir="${project.name}.app" />
		<delete file="${project.name}.dmg" />
		<delete>
			<fileset dir=".">
				<include name="${project.name}-*.zip" />
			</fileset>
		</delete>
	</target>
 
	<!-- COMPILES PROJECT JAR-->
	<target name="compile" depends="1. clean">
		<fail unless="dir.class" message="ERROR: Class dir not defined." />
		<fail unless="dir.lib" message="ERROR: Library dir not defined." />
		<fail unless="dir.libNat" message="ERROR: Library dir for native archives not defined." />
		<fail unless="dir.src" message="ERROR: Source dir not defined." />
		<fail unless="main.class" message="ERROR: Project man class is not defined." />
 
		<!-- Check for render mode, openGL or not -->
		<loadfile property="main" srcfile="${dir.src}/${main.package.path}${main.class}.java" />
		<condition property="is.OPENGL">
			<contains string="${main}" substring="processing.opengl" casesensitive="true" />
		</condition>
		<!-- Check for OS X -->
		<condition property="is.OSX">
			<os family="mac" name="Mac OS X" />
		</condition>
 
		<!-- Create folder for compiled .class documents -->
		<mkdir dir="${dir.class}" />
 
		<!-- Make a temp copy of the main class to add the 
		void main function into it without modifing the original 
		java document in the source folder. -->
		<mkdir dir="temp" />
		<copy todir="temp">
			<fileset dir="${dir.src}">
				<include name="**/*.java" />
			</fileset>
		</copy>
 
		<!-- Add void main(String[] args) -->
		<replaceregexp	file="temp/${main.package.path}${main.class}.java" 
						match="\}([\s\n\t]*)$" 
						replace='public static void main(String args[]) {${line.separator}PApplet.main(new String[] {"${main.package.name}${main.class}"});}${line.separator}}' 
						flags="s" />
 
		<javac srcdir="temp" destdir="${dir.class}" debug="true" deprecation="false" optimize="true" >
			<classpath>
				<fileset dir="${dir.lib}" includes="**/*.jar" />
				<fileset dir="${dir.libNat}" includes="**/*.jar" />
			</classpath>
		</javac>
 
		<delete dir="temp" />
	</target>
 
	<!-- APPLET WEB EXPORT -->
	<target name="4. deploy WEB" depends="compile">
 
		<mkdir dir="${dir.build}" />
		<copy todir="${dir.class}/data">
			<fileset dir="${dir.data}" />
		</copy>
 
		<jar jarfile="${dir.build}/${main.class}.jar">
			<fileset dir="${dir.class}" />
			<manifest>
				<attribute name="Main-Class" value="${main.package.name}${main.class}" />
				<attribute name="Build-By" value="${project.author}" />
				<attribute name="Specification-Title" value="${main.class}" />
				<attribute name="Specification-Version" value="${project.version}" />
			</manifest>
		</jar>
 
		<delete dir="${dir.class}" />
 
		<mkdir dir="${dir.applet}" />
		<copy file="${dir.build}/${main.class}.jar" todir="${dir.applet}" />
		<copy todir="${dir.applet}/src">
			<fileset dir="${dir.src}" includes="**/*.java" />
		</copy>
 
		<pathconvert property="class.pathWeb" pathsep=",">
			<fileset dir="${dir.build}" includes="**/*.jar" excludes="${main.class}.jar" />
			<fileset dir="${dir.lib}">
				<exclude name="*jogl*.*" unless="is.OPENGL" />
				<exclude name="*opengl*.*" unless="is.OPENGL" />
			</fileset>
			<fileset dir="${dir.libNat}">
				<exclude name="**/*jogl*.*" unless="is.OPENGL" />
				<exclude name="**/*.dll" />
				<exclude name="**/*.jnilib" />
			</fileset>
			<flattenmapper />
		</pathconvert>
 
		<delete dir="${dir.build}" />
 
		<antcall target="web.2d" />
		<antcall target="web.opengl" />
	</target>
 
		<!-- WEB openGL TEMPLATE -->
		<target name="web.opengl" if="is.OPENGL">
			<fail unless="class.pathWeb" message="ERROR: Deploy failed, use '4. deploy WEB' instead." />
			<copy todir="${dir.applet}">
				<fileset dir="ant/web" />
				<fileset dir="${dir.lib}">
					<exclude name="**/*jogl*.*" />
				</fileset>
				<fileset dir="${dir.libNat}">
					<exclude name="**/*jogl*.*" />
				</fileset>
			</copy>
 
			<!-- Replace index.html with opengl version -->
			<move file="${dir.applet}/index-opengl.html" tofile="${dir.applet}/index.html" />
 
			<!-- Replace index document tokens -->
			<antcall target="web.template" />
		</target>
 
		<!-- WEB 2D TEMPLATE -->
		<target name="web.2d" unless="is.OPENGL">
			<fail unless="class.pathWeb" message="ERROR: Deploy failed, use '4. deploy WEB' instead." />
			<copy todir="${dir.applet}">
				<fileset dir="ant/web">
					<exclude name="*.jar" />
					<exclude name="*opengl*" />
				</fileset>
				<fileset dir="${dir.lib}" excludes="opengl.jar" />
				<fileset dir="${dir.libNat}" excludes="*jogl*.*" />
			</copy>
			<!-- Replace index document tokens -->
			<antcall target="web.template" />
		</target>
 
		<!-- WEB TEMPLATE TOKEN REPLACE -->
		<target name="web.template">
			<fail message="ERROR: Deploy failed, index.html not reachable. Use '4. deploy WEB' instead.">
				<condition>
					<not>
						<available file="${dir.applet}/index.html" />
					</not>
				</condition>
			</fail>
			<!-- Replace applet/project specific infos -->
			<replace file="${dir.applet}/index.html">
				<replacefilter token="APPLET_HEIGHT" value="${applet.height}" />
				<replacefilter token="APPLET_WIDTH" value="${applet.width}" />
				<replacefilter token="PROJECT_ARCHIVES" value="${main.class}.jar,${class.pathWeb}" />
				<replacefilter token="PROJECT_AUTHOR" value="${project.author}" />
				<replacefilter token="PROJECT_MAIN" value="${main.package.name}${main.class}" />
				<replacefilter token="PROJECT_NAME" value="${project.name}" />
			</replace>
		</target>
 
 
	<!-- EXPORT OSX APPLICATION -->
	<target name="3. deploy OSX" depends="compile">
 
		<!-- Remove existing application -->
		<delete dir="${main.class}.app" />
 
		<fail unless="is.OSX" message="Mac .app deploy works on Mac OS X only." />
 
		<!-- start#BUILD
			Create the project jar document and include all files 
			from the data folder in the root without sub folders.
		-->
		<fail unless="main.class" message="ERROR: ANT Project name not defined. Must be the name of your project man class." />
		<fail unless="project.name" message="ERROR: Project name not defined. Needed for setting  application name." />
 
		<mkdir dir="${dir.build}" />
		<jar jarfile="${dir.build}/${main.class}.jar">
			<fileset dir="${dir.class}" />
			<fileset dir="${dir.data}" />
			<manifest>
				<attribute name="Main-Class" value="${main.package.name}${main.class}" />
				<attribute name="Build-By" value="${project.author}" />
				<attribute name="Specification-Title" value="${main.class}" />
				<attribute name="Specification-Version" value="${project.version}" />
			</manifest>
		</jar>
		<delete dir="${dir.class}" />
		<!-- end#BUILD -->
 
		<!-- Create .app bundle folder structure -->
		<mkdir dir="${project.name}.app" />
		<mkdir dir="${project.name}.app/Contents" />
		<mkdir dir="${project.name}.app/Contents/MacOS" />
		<mkdir dir="${project.name}.app/Contents/Resources" />
		<mkdir dir="${project.name}.app/Contents/Resources/Java" />
 
		<echo file="${project.name}.app/Contents/PkgInfo" message="APPL${project.author.appleId}" />
		<!-- Copy exec JavaApplicationStub without turning it into a txt -->
		<exec executable="cp">
			<arg value="-rp" />
			<arg value="ant/mac/JavaApplicationStub"/>
			<arg value="${project.name}.app/Contents/MacOS" />
		</exec>
 
		<pathconvert property="class.pathApp" pathsep=":$JAVAROOT/">
			<fileset dir="${dir.build}" includes="**/*.jar" />
			<fileset dir="${dir.lib}">
				<exclude name="opengl.jar" unless="is.OPENGL" />
				<exclude name="*jogl*.*" unless="is.OPENGL" />
			</fileset>
			<fileset dir="${dir.libNat}">
				<exclude name="*jogl*.*" unless="is.OPENGL" />
				<exclude name="**/*.jnilib" />
			</fileset>
			<flattenmapper />
		</pathconvert>
 
		<!-- Copy and setup Info.plist-->
		<copy file="ant/mac/Info.plist" todir="${project.name}.app/Contents" />
		<replace file="${project.name}.app/Contents/Info.plist">
			<replacefilter token="APPLEDEV_ID" value="${project.author.appleId}" />
			<replacefilter token="PROJECT_MAIN" value="${main.package.name}${main.class}" />
			<replacefilter token="PROJECT_NAME" value="${project.name}" />
			<replacefilter token="PROJECT_PACKAGE" value="${main.package.name}${main.class}" />
			<replacefilter token="PROJECT_VERSION" value="${project.version}" />
			<replacefilter token="CLASS_PATH" value="$JAVAROOT/${class.pathApp}" />
			<replacefilter token="JVM_VERSION" value="${jvm.version}" />
			<replacefilter token="JVM_XMS" value="${jvm.xms}" />
			<replacefilter token="JVM_XMX" value="${jvm.xmx}" />
			<replacefilter token="OSX_BMLOOK" value="${osx.brushmetal}" />
			<replacefilter token="OSX_PMODE" value="${osx.presentmode}" />
			<replacefilter token="OSX_SAFEPOS" value="${osx.safeposition}" />
		</replace>
 
		<!-- Copy application icon -->
		<copy file="ant/mac/application.icns" todir="${project.name}.app/Contents/Resources/" />
 
		<!-- Copy jar archives -->
		<copy todir="${project.name}.app/Contents/Resources/Java">
			<fileset dir="${dir.build}" includes="**/*.jar" />
			<fileset dir="${dir.lib}">
				<exclude name="opengl.jar" unless="is.OPENGL" />
				<exclude name="**/*jogl*.*" unless="is.OPENGL" />
			</fileset>
			<fileset dir="${dir.libNat}">
				<exclude name="**/*jogl*.*" unless="is.OPENGL" />
			</fileset>
		</copy>
		<!--
 
		REMOVE THIS COMMENT IF YOU HAVE THE XCODE TOOLS INSTALLED.
 
		<exec executable="${osx.devtools}/Tools/SetFile" os="Mac OS X" failifexecutionfails="false">
			<arg value="-a B ${project.name}.app" />
		</exec>
		-->
		<delete dir="${dir.build}" />
 
		<condition property="flg.osx.dmg">
			<matches string="${osx.dmgcopy}" pattern="true" casesensitive="false" />			
		</condition>
		<antcall target="osx.dmg" />
	</target>
 
		<target name="osx.dmg" if="flg.osx.dmg">
 
			<!-- Package working application -->
			<mkdir dir="image/${project.name}.app" />
			<copy todir="image/${project.name}.app">
				<fileset dir="${project.name}.app">
					<exclude name="**/JavaApplicationStub" />
				</fileset>
			</copy>
 
			<!-- Copy exec JavaApplicationStub without turning it into a txt -->
			<exec executable="cp">
				<arg value="-rp" />
				<arg value="${project.name}.app/Contents/MacOS/JavaApplicationStub"/>
				<arg value="image/${project.name}.app/Contents/MacOS" />
			</exec>
 
			<!-- Create disk image -->
			<exec executable="/usr/bin/hdiutil" os="Mac OS X">
				<arg line="create -srcfolder image -volname '${project.name}' image.dmg" />
			</exec>
			<move file="image.dmg" tofile="${project.name}.dmg" />
 
			<delete dir="image" />
		</target>
 
	<!-- EXPORT ZIP COPY OF THE PROJECT -->
	<target name="5. deploy ZIP" description="Exports a zip document 
		containing all important project folders (ant, data, lib, libNative and src). The file 
		name of the document is formated as 'Deploy_yyMMdd.zip' containing the current date.">
 
		<!-- Modifie stamp format for use in info.txt -->
		<tstamp>
			<format property="TSTAMP" pattern="dd-MM-yyyy kk:mm:ss" />
		</tstamp>
 
		<!-- Create info document -->
		<echo file="info.txt">Project: ${project.name}
Date: ${TSTAMP}
Author: ${project.author}${line.separator}
Main-Class: ${main.package.name}${main.class}
Version: ${project.version}${line.separator}
JAVA-VM: ${java.vm.name} ${java.vm.version} ${java.vm.vendor}
JAVA-VER: ${java.version}
ANT: ${ant.version}</echo>
 
		<!-- Modifie stamp format for use in file name -->
		<tstamp>
			<format property="TODAY_STAMP" pattern="yyMMdd" />
		</tstamp>
 
		<zip zipfile="${project.name}-${TODAY_STAMP}.zip">
			<!-- Add project folders -->
			<zipfileset dir="ant" prefix="${project.name}/ant" />
			<zipfileset dir="${dir.data}" prefix="${project.name}/${dir.data}" />
			<zipfileset dir="${dir.lib}" prefix="${project.name}/${dir.lib}" />
			<zipfileset dir="${dir.libNat}" prefix="${project.name}/${dir.libNat}" />
			<zipfileset dir="${dir.src}" prefix="${project.name}/${dir.src}" />
			<!-- Add ANT build file -->
			<zipfileset file="${ant.file}" prefix="${project.name}" />
			<!-- Add project info.txt -->
			<zipfileset file="info.txt" prefix="${project.name}" />
		</zip>
		<delete file="info.txt" />
	</target>
 
</project>

More about ANT

Official

Howto

Books

Ant: The Definitive Guide, 2nd Edition, Steven Holzner, 2005, O'Reilly
Ant in Action (Manning), Steve Loughran, 2007, Manning Publications