Compiling Adobe Flex with Ant
If you really care about your sanity and the sanity of those around you, you probably start most projects by creating a build script of some description. 1 If you've compiled Java or .NET, or if you read the Ant primer then you're probably familiar with writing an XML based build script. Adobe Flex application can be built in the same way, with just a few steps.
Prerequisites
Basic Compilation
We'll start by creating a very basic build.xml file, adding only the lines that are required to get some basic functionality up and running. We'll make it look nice later. After creating an ant project, we need to start by adding two lines: a property definition and a task definition.
<?xml version="1.0" encoding="utf-8"?>
<project name="FlexDemo" default="run">
<property name="FLEX_HOME" value="C:/Program Files/Adobe/Flex SDK 4.5" />
<taskdef resource="flexTasks.tasks" classpath="${FLEX_HOME}/ant/lib/flexTasks.jar"/>
<target name="run">
<delete dir="build" />
<mkdir dir="build" />
</target>
</project>
In order to build a Flex app with Ant, we have to start by defining the location of the Flex SDK in FLEX_HOME2. If you simply copy-paste the directory name from Explorer, assuming you run Windows, you'll notice that it fails rather quickly. You either need to escape all backslashes or use forwardslashes, which is more readable. After setting the location for FLEX_HOME, we use <taskdef> to tell Ant that we have several custom tasks that are defined by the flex SDK in the file flexTasks.jar.
Though adding Adobe's custom Ant tasks gives us access to several new tasks, for right now we only care about two: compc, which is used to build swcs, and mxmlc, which is used for building swfs. We can go ahead and use the <mxmlc> task to compile a swf.
Using MXMLC
Assuming that we have a basic Flex project that does not need any libraries, we can start going in just one line extra line of xml. We just need to tell the compiler what mxml file to use and where to put the resultant swf.
<mxmlc file="exampleApp/example.mxml" output="build/example.swf" />
Though that one line is all that is strictly necessary to compile a project, there are several things that we can do to improve things. To start with, though this will work fine if we are only compiling one or two swfs, we might notice that with every swf we compile, Ant's memory usage goes a little higher, never to return again. Why? By default <mxmlc> is just an extension of <jar>. Thus, every compiler invocation takes place in the same JVM that we use for Ant. The compiler has a few memory leaks that wouldn't normally be too visible because the JVM would be recycled at the end of the compilation process. Since that doesn't happen here, the memory footprint stedily increases with each run. To prevent Ant from ballooning due to memory leaks, we'll add an argument that will be used by the base <jar> task that will cause the compiler to execute in a seperate JVM. This will slightly slow the compilation process, but it should prevent a long running Ant script from failing because of memory. 3
<mxmlc file="exampleApp/example.mxml" output="build/example.swf" fork="true" />
Our next change is to disable incremental compilation. Its purpose is to speed the compilation process by avoiding recompilation on parts of a swf where the underlying code is unchanged. In the best case, that does seem to be true. In a number of cases it drastically slows the compilation process down, however. Additionally, there's been at least a few times noted when it did not detect that underlying code had changed, causing time to be wasted by pointlessly debugging. Personally, the risk appears to be greater than the return, so we will disable it.
<mxmlc file="exampleApp/example.mxml" output="build/example.swf" fork="true" incremental="false" />
Though we've already got <mxmlc> compiling for rather simple swfs, any real-world project is likely to have a few libraries that it depends on. In order to specify them, we'll need to add a <library-path> inside our <mxml> task. In this example, Ant is looking inside of the build directory for any swcs that it might need. Though we could list them off one by one, in this case we'll use the assumption that if we have them in the build directory, it must be because we need them. 4
<mxmlc file="exampleApp/example.mxml" output="build/example.swf" fork="true" incremental="false">
<library-path dir="build" append="true">
<include name="*.swc" />
</library-path>
</mxmlc>
Adding COMPC
Though it's rather useful to build .swfs, many projects will likely require that at least one library is built as well. This won't be a problem though; Building a library is just as easy as building a swc. To use the compc task, we only need specify the folder containing our sources, the classes that should be compiled and the directory that the resultant swc should be placed. Since we almost always want to compile all of the classes in our library, we can just provide an asterisk to the compiler.
<compc output="build/exampleLibrary.swc">
<source-path path-element="exampleLibrary/src" />
<include-sources dir="exampleLibrary/src" includes="*" />
</compc>
Completed Example
<?xml version="1.0" encoding="utf-8"?>
<project name="FlexDemo" default="run">
<property name="FLEX_HOME" value="/home/joshua/Source/SDKs/Flex/4.5.1" />
<taskdef resource="flexTasks.tasks" classpath="${FLEX_HOME}/ant/lib/flexTasks.jar"/>
<target name="run">
<!-- Setup -->
<delete dir="build" />
<mkdir dir="build" />
<!-- Compile our library -->
<compc output="build/exampleLibrary.swc" incremental="false" fork="true">
<source-path path-element="exampleLibrary/src" />
<include-sources dir="exampleLibrary/src" includes="*" />
</compc>
<!-- Use the library and the project .mxml to build our final .swf -->
<mxmlc output="build/example.swf" file="exampleApp/example.mxml" incremental="false" fork="true">
<library-path dir="build" append="true">
<include name="*.swc" />
</library-path>
</mxmlc>
</target>
</project>
Unless you like having most of your conversations end with “well that's odd, it compiles / works on my machine.” ↩︎
If you've got just a little OCD, it's probably annoying you that FLEX_HOME uses a competely different naming convention than most Ant variable names. At least if it wasn't, it is now. ↩︎
I haven't tried running without “fork” since Flex 3.5, but I think it is rather likely that this leak still exists. ↩︎
In general this seems to be a safe assumption to make. The mxmlc compiler is smart enough to not try and link any libraries or parts of libraries that are not needed by your application. That isn't without its own issues, but we can cover those another day. ↩︎