Your site looks fat in those SWF’s! Trimming the bytecode with ANT and FlexBuilder
August 4th, 2009Complex Flash interactives are often composed of a series of small, modular swf's being loaded and choreographed by a primary shell ("Shell.swf"). This way, the smaller modular swf's ("Module.swf") get loaded in an on-demand fashion, to obvious advantages. Traditionally these seperate swf's create a bit of overlap in terms of which classes get compiled into which swf, leading to a moderate-to-profound degree of redundant information in each swf's byte footprint. Using ANT to compile your modules (swf's) you can tell the compiler to ignore any classes already included in Shell.swf, leaving them out of the Module.swf and resulting in a leaner, meaner byte footprint. The savings can be very significant, but its worth pointing out that any classes in the flash.* packages don't get compiled into your swf at all as they are cached within the player itself (Flash 9.3 can also cache the Flex framework, keeping it out of your swf by using magic, but never mind that for now). The trade-off is that if you try to run Module.swf as a stand-alone, Flash will give you a popup error telling you that it doesn't know what you're talking about and that it doesn't like you anymore and who the hell built this thing anyway.
ANT accomplishes these wonders by generating something called a "link report" when it compiles both Shell.swf and Module.swf. The link report is an XML file that lists what got compiled into the swf, along with its prerequisites ("<pre>" XML elements, pointing at other definitions for classes that must be compiled before you can compile "this" class) and dependencies ("<dep>" XML elements, which are the reverse of "<pre>" and point to classes that will depend on "this" class being compiled first). But the real fun comes at the end of the link report, where you'll find the "<external-defs>" XML element and child "<ext>" nodes which point to the classes that should be found in the containing swf, either Shell.swf or the flash player itself. When ANT, configured correctly, compiles the Module.swf, it checks the "<pre>" nodes against the "<ext>" nodes, and if they match then guess what? They are excluded from the swf's bytecode. Your swf is now smaller.
Getting ANT to do this is a smaller deal than it might sound (but install it first). ANT requires 2 files to work properly, build.properties and build.xml. First I'll cover build.propertis. Build.properties defines a bunch of command-line compiler arguments, like classpaths, default swf size and framerate, and any other variables you need to define so you can reference in build.xml- for instance, where to put your link reports and where to spit out the compiled swf's. Here's an example of how I set up my build.properties:
-
#this file, build.properties, is defining a bunch of variables that are used in build.xml, described below.
-
-
# necessary directions to various parts of Flash/Flex that are besides the point that we're making here:
-
flex3dir = C:/Program Files/Adobe/Flex Builder 3
-
flex3bindir = ${flex3dir}/sdks/3.0.0/bin
-
flex3libsdir = ${flex3dir}/sdks/3.0.0/frameworks/libs mxmlc = ${flex3bindir}/mxmlc.exe
-
asdoc = ${flex3bindir}/asdoc.exe
-
flashplayer = ${flex3dir}/sdks/3.0.0/runtimes/player/win/FlashPlayer.exe
-
# SWF defaults, also besides the point:
-
framerate = 31
-
bgcolor = 0xFFFFFF
-
width = 1024
-
height = 768
-
# Project paths
-
classesdir = ${basedir}/as
-
approotdir = ${classesdir}
-
deploydir = ${basedir}/bin
-
#as3_classes point the compiler at where it will find playerglobal.swc, which provides definitions for flash.* classes
-
#Normally this path should point to somewhere in Flash CS4's install directory but I couldn't find it there for some reason.
-
as3_classes = C:/Users/Spaceninja/lib/AS3_Classes
-
docRootClassName = Main.as
-
#here's the only sexy part of the file, which points to where the compiler will dump the above mentioned linkreport.xml
-
linkreportsdir = ${basedir}/ant_stuff/link_reports
-
#shellLinkReport tacks on 1 more subdirectory to 'linkreportsdir,' as it is good form to have shell link reports in a separate
-
#directory from the module link reports. this line also tells the compiler to name the link report "main.xml" (the ".xml" is implied)
-
shellLinkReport = ${linkreportsdir}/shell/main
-
#sexy part over
-
-
# Command-line arguments to the flash compiler, build.xml will add more such arguments but don't worry about that
-
baseCompileFlags = -optimize=true \
-
-strict=true \
-
-incremental=true \
-
-debug=false \
-
-verbose-stacktraces=false \
-
-default-background-color=${bgcolor} \
-
-default-frame-rate=${framerate}
-
baseCompilePaths = -source-path '${classesdir}' \
-
-source-path '${as3_classes}' \
-
-library-path '${flex3libsdir}'
Now let's see how build.xml uses the above nonsense to get some work done. Build.xml files declare one "<project>" XML element that will contain at least one "<target>" XML element. The <project> element usually looks like this:
-
<project name="MyProject" default="compileEverything" basedir="..">
As you can see, it defines a name for the project and points to the default The <target> nodes define the work that gets done- they're the "what gets run" in your build file. A build.xml file can have any number of targets, and targets can depend on other targets. They can also reference any variable defined in our build.properties file. Sometimes targets just define a couple <property> nodes to pass to other, more ambitious targets that will execute our compiler. (ANT can do more than just execute a compiler but its close enough for our purposes. Or at least my purposes.) Here's an example of the "compileEverything" target XML node. When it runs it just calls the other 2 nodes, compiling both swf's in one process: If you don't want to compile everything in one go, you can run the targets individually. As long as "compileShell" has run previously there will still be an xml link report file for "compileModule" to mooch its classes off of. The main take-away from the above xml is near the end, where it says "-load-externs '${shellLinkReport}'." This tells it to compile the module swf as if it can expect to find all the classes that overlap with shell in the shell swf.