Building an AIR Application with ANT

Building on my previous Continuous Integration series I felt there were a few more points that I needed to cover to help those others that may be running into some roadblocks with their Continuous Integration scripts.

Let’s get into have to build and package an AIR application using ANT. Unlike building a web application you need more than just a SWF file to deploy your application. You will need to actually build the SWF file and then package that SWF file into an ANT file.

The Three (not so easy) Steps:
1. Build a SWF File
2. Make a Certificate
3. Package your SWF with your Certificate

Starting Out
I’ve created a simple AIR Project called “AIRCI”.
AIRCI Project Package Explorer

Right now the AIRCI.mxml file is super super simple.

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                       xmlns:s="library://ns.adobe.com/flex/spark"
                       xmlns:mx="library://ns.adobe.com/flex/mx">
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>
    <s:layout>
        <s:VerticalLayout gap="20"
                          paddingBottom="20"
                          paddingLeft="20"
                          paddingRight="20"
                          paddingTop="20"/>
    </s:layout>
    <s:Rect width="100%" height="100%">
        <s:fill>
            <s:SolidColor color="0xFF0000"/>
        </s:fill>
    </s:Rect>
    <s:Rect width="100%" height="100%">
        <s:fill>
            <s:SolidColor color="0x00FF00"/>
        </s:fill>
    </s:Rect>
    <s:Rect width="100%" height="100%">
        <s:fill>
            <s:SolidColor color="0x0000FF"/>
        </s:fill>
    </s:Rect>
</s:WindowedApplication>

And the app descriptor file that Flash Builder generated.

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<application xmlns="http://ns.adobe.com/air/application/1.5.3">

<!-- Adobe AIR Application Descriptor File Template.

    Specifies parameters for identifying, installing, and launching AIR applications.

    xmlns - The Adobe AIR namespace: http://ns.adobe.com/air/application/1.5.3
            The last segment of the namespace specifies the version
            of the AIR runtime required for this application to run.

    minimumPatchLevel - The minimum patch level of the AIR runtime required to run
            the application. Optional.
-->

    <!-- The application identifier string, unique to this application. Required. -->
    <id>AIRCI</id>

    <!-- Used as the filename for the application. Required. -->
    <filename>AIRCI</filename>

    <!-- The name that is displayed in the AIR application installer.
         May have multiple values for each language. See samples or xsd schema file. Optional. -->
    <name>AIRCI</name>

    <!-- An application version designator (such as "v1", "2.5", or "Alpha 1"). Required. -->
    <version>v1</version>

    <!-- Description, displayed in the AIR application installer.
         May have multiple values for each language. See samples or xsd schema file. Optional. -->
    <!-- <description></description> -->

    <!-- Copyright information. Optional -->
    <!-- <copyright></copyright> -->

    <!-- Publisher ID. Used if you're updating an application created prior to 1.5.3 -->
    <!-- <publisherID></publisherID> -->

    <!-- Settings for the application'
s initial window. Required. -->
    <initialWindow>
        <!-- The main SWF or HTML file of the application. Required. -->
        <!-- Note: In Flash Builder, the SWF reference is set automatically. -->
        <content>[This value will be overwritten by Flash Builder in the output app.xml]</content>

        <!-- The title of the main window. Optional. -->
        <!-- <title></title> -->

        <!-- The type of system chrome to use (either "standard" or "none"). Optional. Default standard. -->
        <!-- <systemChrome></systemChrome> -->

        <!-- Whether the window is transparent. Only applicable when systemChrome is none. Optional. Default false. -->
        <!-- <transparent></transparent> -->

        <!-- Whether the window is initially visible. Optional. Default false. -->
        <!-- <visible></visible> -->

        <!-- Whether the user can minimize the window. Optional. Default true. -->
        <!-- <minimizable></minimizable> -->

        <!-- Whether the user can maximize the window. Optional. Default true. -->
        <!-- <maximizable></maximizable> -->

        <!-- Whether the user can resize the window. Optional. Default true. -->
        <!-- <resizable></resizable> -->

        <!-- The window's initial width. Optional. -->
        <!-- <width></width> -->

        <!-- The window'
s initial height. Optional. -->
        <!-- <height></height> -->

        <!-- The window's initial x position. Optional. -->
        <!-- <x></x> -->

        <!-- The window'
s initial y position. Optional. -->
        <!-- <y></y> -->

        <!-- The window's minimum size, specified as a width/height pair, such as "400 200". Optional. -->
        <!-- <minSize></minSize> -->

        <!-- The window'
s initial maximum size, specified as a width/height pair, such as "1600 1200". Optional. -->
        <!-- <maxSize></maxSize> -->
    </initialWindow>

    <!-- The subpath of the standard default installation location to use. Optional. -->
    <!-- <installFolder></installFolder> -->

    <!-- The subpath of the Programs menu to use. (Ignored on operating systems without a Programs menu.) Optional. -->
    <!-- <programMenuFolder></programMenuFolder> -->

    <!-- The icon the system uses for the application. For at least one resolution,
         specify the path to a PNG file included in the AIR package. Optional. -->
    <!-- <icon>
        <image16x16></image16x16>
        <image32x32></image32x32>
        <image48x48></image48x48>
        <image128x128></image128x128>
    </icon> -->

    <!-- Whether the application handles the update when a user double-clicks an update version
    of the AIR file (true), or the default AIR application installer handles the update (false).
    Optional. Default false. -->
    <!-- <customUpdateUI></customUpdateUI> -->

    <!-- Whether the application can be launched when the user clicks a link in a web browser.
    Optional. Default false. -->
    <!-- <allowBrowserInvocation></allowBrowserInvocation> -->

    <!-- Listing of file types for which the application can register. Optional. -->
    <!-- <fileTypes> -->

        <!-- Defines one file type. Optional. -->
        <!-- <fileType> -->

            <!-- The name that the system displays for the registered file type. Required. -->
            <!-- <name></name> -->

            <!-- The extension to register. Required. -->
            <!-- <extension></extension> -->

            <!-- The description of the file type. Optional. -->
            <!-- <description></description> -->

            <!-- The MIME content type. -->
            <!-- <contentType></contentType> -->

            <!-- The icon to display for the file type. Optional. -->
            <!-- <icon>
                <image16x16></image16x16>
                <image32x32></image32x32>
                <image48x48></image48x48>
                <image128x128></image128x128>
            </icon> -->

        <!-- </fileType> -->
    <!-- </fileTypes> -->

</application>

The build.xml file looks like…

<?xml version="1.0" encoding="UTF-8"?>
<project name="Build File" basedir="." default="build">

    <!--location of property file -->
    <property file="${basedir}/build.properties" description="your specific properities for builds" />

    <!-- additional tasks -->
    <taskdef name="mxmlc" classname="flex.ant.MxmlcTask" classpath="${FLEX_TASKS}"/>
    <taskdef name="compc" classname="flex.ant.CompcTask" classpath="${FLEX_TASKS}"/>
    <taskdef name="asdoc" classname="flex.ant.AsDocTask" classpath="${FLEX_TASKS}"/>

</project>

And the build.properties file…

#Flex Locations
FLEX_HOME=/Applications/Adobe\ Flash\ Builder\ 4/sdks/4.0.0
FLEX_TASKS=${FLEX_HOME}/ant/lib/flexTasks.jar

Build the SWF File
When building an AIR application we start by building the SWF file and then storing that build file to package with the certificate.

What I’ve done here is use the FlexTasks MXMLC task to build the SWF file. I’ve specified that this is an AIR build so that we use the air-config.xml provided by Adobe. This provides lots of settings for us such as manifest files and other settings. Then I specify the source folder, library folder, what file is the main application file, and finally where the built swf file will be output to.

    <target name="compile"
        description="Compiles the AIR application to a SWF file and places SWF in a temp directory to be packaged.">
        <mxmlc file="${SOURCE_DIR}/${APP_NAME}.${APP_EXTENSION}"
            output="${BUILD_DIR}/${APP_NAME}.swf"
            locale="${LOCALE}"
            static-rsls="true"
            accessible="true"
            configname="air"
            debug="${DEBUG_FLAG}"
            failonerror="true"
            fork="true"
            maxmemory="512m">
            <source-path path-element="${SOURCE_DIR}"/>
            <external-library-path file="${FLEX_HOME}/frameworks/libs/air/airglobal.swc" append="true"/>
            <library-path dir="${LIBRARY_DIR}" includes="*.swc" append="true"/>
        </mxmlc>
    </target>

And the properties’s file additions:

#App Settings
APP_NAME=AIRCI
APP_EXTENSION=mxml
SOURCE_DIR=${basedir}/src
LIBRARY_DIR=${basedir}/libs
RELEASE_DIR=${basedir}/release
BUILD_DIR=${basedir}/build

#Etc Settings
LOCALE=en_US
DEBUG_FLAG=false

Make the Certificate
Next we need to build the certificate – especially if the certificate hasn’t been made yet. In the final file we’ll check if it exists and only create the certificate if it doesn’t, but for now we’ll just build the file. We’ll use the ADT executable to achieve the certificate creation.

    <target name="certificate" unless="CERTIFICATE_FLAG">
        <java jar="${ADT}" fork="true"
            failonerror="true">
            <arg value="-certificate"/>
            <arg value="-cn"/>
            <arg value="${CERT_NAME}"/>
            <arg value="-ou"/>
            <arg value="${CERT_ORG_UNIT}"/>
            <arg value="-o"/>
            <arg value="${CERT_ORG_NAME}"/>
            <arg value="-c"/>
            <arg value="${CERT_COUNTRY}"/>
            <arg value="${CERT_KEY_TYPE}"/>
            <arg value="${KEYSTORE}"/>
            <arg value="${CERT_PASSWORD}"/>
        </java>
    </target>

And the build properties added in this step:

#certificate
STORETYPE=pkcs12
KEYSTORE=cert.p12
CERT_NAME=UnitedMindsetSelfSignedCertificate
CERT_ORG_UNIT=Software Development
CERT_ORG_NAME=UnitedMindset
CERT_COUNTRY=US
CERT_KEY_TYPE=2048-RSA
CERT_PASSWORD=unitedmindsetNotARealPassword

Package and Release the AIR Application
Finally we’ll create the releasable application using the ADT executable with the application descriptor file, the swf file that was previously created, and the certificate.

    <target name="package" depends="compile, certificate"
        description="Packages the build SWF file from a temp directory.">
        <java jar="${ADT}" fork="true"
            failonerror="true"
            maxmemory="512m">
            <arg value="-package"/>
            <arg value="-storetype"/>
            <arg value="${STORETYPE}"/>
            <arg value="-keystore"/>
            <arg value="${KEYSTORE}"/>
            <arg value="-storepass"/>
            <arg value="${CERT_PASSWORD}"/>
            <arg value="${RELEASE_DIR}/${APP_NAME}.air"/>
            <arg value="${SOURCE_DIR}/${APP_NAME}-app.xml"/>
            <arg value="-C"/>
            <arg value="${BUILD_DIR}"/>
            <arg value="${BUILD_DIR}/${APP_NAME}.swf"/>
        </java>
    </target>

No additions were added to the build.properties file.

Final build.xml File

<?xml version="1.0" encoding="UTF-8"?>
<project name="Build File" basedir="." default="build">

    <!--location of property file -->
    <property file="${basedir}/build.properties" description="your specific properities for builds" />

    <!-- additional tasks -->
    <taskdef name="mxmlc" classname="flex.ant.MxmlcTask" classpath="${FLEX_TASKS}"/>
    <taskdef name="compc" classname="flex.ant.CompcTask" classpath="${FLEX_TASKS}"/>
    <taskdef name="asdoc" classname="flex.ant.AsDocTask" classpath="${FLEX_TASKS}"/>

    <!--


       Build


   -->

    <target name="build" description="compiles application">
        <antcall target="init"/>
        <antcall target="package"/>
        <antcall target="cleanup"/>
    </target>

    <target name="compile"
        description="Compiles the AIR application to a SWF file and places SWF in a temp directory to be packaged.">
        <mxmlc file="${SOURCE_DIR}/${APP_NAME}.${APP_EXTENSION}"
            output="${BUILD_DIR}/${APP_NAME}.swf"
            locale="${LOCALE}"
            static-rsls="true"
            accessible="true"
            configname="air"
            debug="${DEBUG_FLAG}"
            failonerror="true"
            fork="true"
            maxmemory="512m">
            <source-path path-element="${SOURCE_DIR}"/>
            <external-library-path file="${FLEX_HOME}/frameworks/libs/air/airglobal.swc" append="true"/>
            <library-path dir="${LIBRARY_DIR}" includes="*.swc" append="true"/>
        </mxmlc>
    </target>

    <target name="certificate" unless="CERTIFICATE_FLAG">
        <java jar="${ADT}" fork="true"
            failonerror="true">
            <arg value="-certificate"/>
            <arg value="-cn"/>
            <arg value="${CERT_NAME}"/>
            <arg value="-ou"/>
            <arg value="${CERT_ORG_UNIT}"/>
            <arg value="-o"/>
            <arg value="${CERT_ORG_NAME}"/>
            <arg value="-c"/>
            <arg value="${CERT_COUNTRY}"/>
            <arg value="${CERT_KEY_TYPE}"/>
            <arg value="${KEYSTORE}"/>
            <arg value="${CERT_PASSWORD}"/>
        </java>
    </target>

    <target name="package" depends="compile, certificate"
        description="Packages the build SWF file from a temp directory.">
        <java jar="${ADT}" fork="true"
            failonerror="true"
            maxmemory="512m">
            <arg value="-package"/>
            <arg value="-storetype"/>
            <arg value="${STORETYPE}"/>
            <arg value="-keystore"/>
            <arg value="${KEYSTORE}"/>
            <arg value="-storepass"/>
            <arg value="${CERT_PASSWORD}"/>
            <arg value="${RELEASE_DIR}/${APP_NAME}.air"/>
            <arg value="${SOURCE_DIR}/${APP_NAME}-app.xml"/>
            <arg value="-C"/>
            <arg value="${BUILD_DIR}"/>
            <arg value="${BUILD_DIR}/${APP_NAME}.swf"/>
        </java>
    </target>

    <target name="init" depends="clean"
    description="Cleans the deploy file">
        <mkdir dir="${BUILD_DIR}"/>
        <mkdir dir="${RELEASE_DIR}"/>
        <available file="${KEYSTORE}" property="CERTIFICATE_FLAG"/>
    </target>

    <target name="clean"
        description="Cleans up old files.">
        <delete dir="${BUILD_DIR}" failOnError="false" includeEmptyDirs="true" />
        <delete dir="${RELEASE_DIR}" failOnError="false" includeEmptyDirs="true" />
    </target>

    <target name="cleanup"
        description="Cleans up old files.">
        <delete dir="${BUILD_DIR}" failOnError="false" includeEmptyDirs="true" />
    </target>

</project>

Final build.properties File

#Flex Locations
FLEX_HOME=/Applications/Adobe\ Flash\ Builder\ 4/sdks/4.0.0
FLEX_TASKS=${FLEX_HOME}/ant/lib/flexTasks.jar

#Compilers
ADL=${FLEX_HOME}/bin/adl
ADT=${FLEX_HOME}/lib/adt.jar

#App Settings
APP_NAME=AIRCI
APP_EXTENSION=mxml
SOURCE_DIR=${basedir}/src
LIBRARY_DIR=${basedir}/libs
RELEASE_DIR=${basedir}/release
BUILD_DIR=${basedir}/build

#Etc Settings
LOCALE=en_US
DEBUG_FLAG=false

#certificate
STORETYPE=pkcs12
KEYSTORE=cert.p12
CERT_NAME=UnitedMindsetSelfSignedCertificate
CERT_ORG_UNIT=Software Development
CERT_ORG_NAME=UnitedMindset
CERT_COUNTRY=US
CERT_KEY_TYPE=2048-RSA
CERT_PASSWORD=unitedMindsetNotARealPassword

One error you may see when trying to package your application is:
[java] /Users/jonbcampos/Documents/work/dflex/AIRCI/src/AIRCI-app.xml(43): error 105: application.initialWindow.content contains an invalid value

This is because the content in your application descriptor file isn’t set. When Flash Builder builds an AIR file it replaces the content tag in the application descriptor file.

<!-- Note: In Flash Builder, the SWF reference is set automatically. -->
<content>[This value will be overwritten by Flash Builder in the output app.xml]</content>

You may want to just switch it out for the name of your swf file:

<content>AIRCI.swf</content>
Share

Comments (9)

[...] This post was mentioned on Twitter by CT, karannnnnnnnnnn3. karannnnnnnnnnn3 said: Building an AIR Application with ANT: Building on my previous Continuous Integration series I felt there were a fe… http://bit.ly/bvtClp [...]

Ariel JakobovitsAugust 12th, 2010 at 5:15 pm

very nice post

AbhijitOctober 20th, 2010 at 4:08 pm

I am getting this error while running package target.
package:
[java] Could not generate timestamp: Connection refused: connect

It would be great help if anybody provide any hint for this issue fix.

Jonathan CamposOctober 21st, 2010 at 10:05 am

@Abhijit Very interesting issue. You could take out anything that deals with the timestamp. I am guessing there is an issue with your JVM.

RobFebruary 2nd, 2011 at 12:05 pm

great article, works all fine so far, just can’t use any file access within my application. compiler says “cannot resolve: File”, seems it does not include some flash.filesystem libs.

jonbcamposFebruary 2nd, 2011 at 12:23 pm

@Rob, that is strange. I am guessing it is an issue with ant. Make sure that your ant is updated.

JPFebruary 2nd, 2011 at 10:33 pm

Jonathan,

Many thanks for this post — it helped me isolate what appears to be an obscure AIR bug. My organization has built a number of these ANT setups for AIR apps over the years, but recently an ANT-built app started displaying very strange behavior:

When launched on Windows 7 for the first time, the app behaved as expected. But on subsequent launches, the app had no network connectivity at all. All network requests failed (returning no-information Error 2032 IOErrors), and the URLMonitor class believed the app was disconnected. Using Charles to sniff HTTP requests, absolutely nothing was coming from the app. I could uninstall and reinstall the app, yet the problem persisted. Even removing its Local Store files and combing for registry keys made no difference. Until a new version of the app was installed, there was no connectivity whatsoever. Installing a new version always “fixed” it for just one launch. It didn’t even matter if any network access took place on that first launch; any network access was simply DOA on launch #2+. We could reproduce this on totally clean Win7 x64 systems. Some users reported it on x86; but Mac was never affected. (Not sure about other Windows platforms.) The strangest thing was that another stripped-down test app with the same network code behaved completely normally.

I soon realized that the problem related to where the release-quality SWF came from — if the SWF was generated from Flash Builder via Export Release Build, everything went fine. If I generated it using ANT and mxmlc, however, it choked. Comparing the SWF file sizes, the ANT-generated was barely half the size of the Flash Builder-generated. This seemed really odd, since the app behaved identically except for the connectivity issue on windows. When comparing the SWFs in ASV, large chunks of the Flex framework seemed to be omitted from the pared-down ANT SWF, but present in the Flash Builder SWF.

We use the -load-config directive in combination with the mxmlc-dumped config file from Flash Builder (-dump-config). Combing through the mxmlc config file, debug and other options were all set properly. I wondered if RSLs could somehow be an issue, as the .actionScriptProperties file had some directives related to this.

That’s when I discovered this post. Comparing your ANT code and mine, the only difference was:

static-rsls=”true”

So, I returned to the config file and changed

false

To:

true

Re-building the SWF, it was now within 1K of the FlashBuilder-generated SWF’s size. After we ran a full build and installed it, everything functioned perfectly.

I hope that this experience will save someone else some time! You’d think the SWF would have failed everywhere, or failed in a more “expected” way, but no… just a highly-obscure network issue. I figure now that the SWF must have somehow become “blacklisted” against the Flash Player when there was the RSL issue.

If you or anyone else can shed any light on the “why” behind this, I’d appreciate it.

Regards,
JP

jonbcamposFebruary 3rd, 2011 at 11:06 am

@JR I don’t have a cut and dry explanation for you. I would just guess that sense the app is using the RSLs rather than what it was built with you may be dealing with different handling code. I’m glad it got working for you though.

adobe air 105 build error | Air DataApril 19th, 2011 at 8:18 am

[...] Building an AIR Application with ANT | Jonathan Campos' Blog Jun 22, 2010 … Adobe AIR Application Descriptor File Template. … xmlns – The Adobe AIR namespace: …. I've specified that this is an AIR build so that we ….. error 105: application.initialWindow.content contains an invalid value … [...]

Leave a comment

Your comment