Sunday, June 21. 2009
Runtime Shared Libraries (RSL). In the picture on the right you can see the module structure of the whole build, which was done using Maven and the flex-mojos plugin.
You can see that there is one high level library (library A) that is shared across all applications and a more specialized library (library B) that is providing components and classes to a certain subset of other applications.
Library A contains a couple of embedded images containing various icons and a lot of components, while Library B contained just a few components. So after compilation the obvious expected result would be to have an RSL file of library A that is much bigger than the RSL file of library B in case library B uses library A as RSL and not as statically linked SWC.
Well the dynamic linkage worked well for all the applications, which where just a couple of KBs in size after the change in contrast to more than 1 MB before the change. However this was not the case for the libraries. Library A had a size of 1.2 MB but library B had a size of 1.25 MB???
So it looks like the library B Maven project was not set up correctly and is stilled compiled statically against library A. But before we continue let's strip this problem down to a more simple project to be able to examine this in a clean environment. Feel free to download the project and try it out yourself.
Analyzation of the Problem
This simple project contains three modules.
The library lib-a contains a big image that is an embedded resource in the MXML component ImageComponent. This library contains also a couple of other sources that have no other purpose than to increase the size of the library. The flex-mojos configuration defines the 'optimize' goal in its <execution> configuration. This tells the compiler to create a RSL from the generated SWC.
Like Library lib-a, lib-b is also configured to compile into an RSL. It contains another MXML component UsingLibAComponent that uses the ImageComponent from lib-a and therefore has a dependency on that library. This dependency is actually configured as RSL, just like it is done by the application app, which simply displays the component from lib-b.
The library lib-a and the application are only of secondary interest here. The size problem is only in the library lib-b so let's focus on that module.
Have a look into the pom.xml file of lib-b. You'll notice that the scope of the lib-a dependency is really set to rsl. This tells flex-mojos to tell the flex compiler to use this library as RSL dependency.
<dependencies> <dependency> <groupId>de.yeap.playground.rsls</groupId> <artifactId>lib-a</artifactId> <version>1.0-SNAPSHOT</version> <type>swc</type> <scope>rsl</scope> </dependency> </dependencies>
And in fact flex-mojos seems to behave correctly when it calls the compiler from a mvn clean install. So I do not see a Bug in flex-mojos here.
[INFO] Flex compiler configurations: -compiler.accessible=false -compiler.actionscript-file-encoding UTF-8 -compiler.allow-source-path-overlap=false -compiler.as3=true -compiler.debug=true -compiler.es=false -compiler.external-library-path /home/cschlipf/tmp/rsltest/lib-b/target/classes/libraries/playerglobal.swc -compiler.fonts.local-fonts-snapshot /home/cschlipf/tmp/rsltest/lib-b/target/classes/fonts.ser -compiler.headless-server=false -compiler.include-libraries= -compiler.keep-all-type-selectors=false -compiler.keep-generated-actionscript=false -compiler.library-path /home/cschlipf/.m2/repository/com/adobe/flex/framework/flex/220.127.116.1158/flex-18.104.22.16858.swc /home/cschlipf/.m2/repository/com/adobe/flex/framework/rpc/22.214.171.12458/rpc-126.96.36.19958.swc /home/cschlipf/.m2/repository/com/adobe/flex/framework/utilities/188.8.131.5258/utilities-184.108.40.20658.swc /home/cschlipf/.m2/repository/com/adobe/flex/framework/framework/220.127.116.1152/framework-18.104.22.16852.swc /home/cschlipf/.m2/repository/de/yeap/playground/rsls/lib-a/1.0-SNAPSHOT/lib-a-1.0-SNAPSHOT.swc -compiler.locale= -compiler.namespaces.namespace http://www.adobe.com/2006/mxml /home/cschlipf/tmp/rsltest/lib-b/target/classes/configs/mxml-manifest.xml -compiler.optimize=true -compiler.source-path /home/cschlipf/tmp/rsltest/lib-b/src/main/flex -compiler.strict=true -compiler.use-resource-bundle-metadata=true -compiler.verbose-stacktraces=false -compute-digest=true -default-background-color 8821927 -default-frame-rate 24 -default-script-limits 1000 60 -default-size 500 375 -metadata.date Fri Jun 19 23:23:11 CEST 2009 -runtime-shared-library-path=/home/cschlipf/.m2/repository/com/adobe/flex/framework/framework/22.214.171.12452/framework-126.96.36.19952.swc,framework-188.8.131.5252.swz, -runtime-shared-library-path=/home/cschlipf/.m2/repository/de/yeap/playground/rsls/lib-a/1.0-SNAPSHOT/lib-a-1.0-SNAPSHOT.swc,lib-a-1.0-SNAPSHOT.swf, -target-player 9.0.124 -use-network=true
As you can see here lib-a is correctly added to the compiler.library-path and the runtime-shared-library-path options - just like the framework library. But here are the resulting file sizes:
[INFO] Original file is: 10095 k bytes [INFO] optimized swf is: 606 k bytes
[INFO] Original file is: 784 k [INFO] optimized swf is: 541 k bytes
So lib-b is almost as big as lib-a although it contains just one class, while lib-a contains 1000 much bigger classes and an embedded big image resource. Looks like lib-b is statically compiled against lib-a.That's why it is a little bit smaller than lib-a as the 1000 source files contained in the lib-a are not used by lib-b and are therefore not linked against lib-b. But let's double check this by uncommenting the scope from the lib-a dependency and see what happens with lib-b in case we compile it statically against lib-a by intention.
[INFO] Original file is: 784 k bytes [INFO] optimized swf is: 541 k bytes
Exactly the same numbers and yes... I double checked that lib-a is no longer specified to the compiler through the runtime-shared-library-path option.
And the Application?
All the time I was talking about the libraries, but what's about the application, which is also compiled against the RSL libraries? It's size is 72 KB. So obviously the application is dynamically linked against the libraries just like we had expected for lib-b being dynamically compiled against lib-a.
In fact there seems so be a bug in the compiler when linking libraries against dynamically linked RSLs. Libraries are always linked statically against other libraries - no matter whether specified through the runtime-shared-library-path option or not. This does not apply to applications, where the compiler behaved correctly.
But what can you do against that? The solutions is quite simple:
For libraries externalize all dependencies that are dynamically loaded by the application and make sure that the application loads all required libraries through linking the application against all dependent (inkl. transitive dependent) RSLs.
So do not use the scope 'rsl', but rather use 'external' for dependencies in SWC projects:
<dependencies> <dependency> <groupId>de.yeap.playground.rsls</groupId> <artifactId>lib-a</artifactId> <version>1.0-SNAPSHOT</version> <type>swc</type> <scope>external</scope> </dependency> </dependencies>
To be able to see the result of externalizing dynamic libraries for libraries see this table:
|Configuration||Size of lib-a
||Size of lib-b
|Externalized framework.swc and lib-a for lib-b
|Size saving in KB
|Size saving in %
Feel free to download my sample application (11.9 MB) to verify my results. You will need to have Maven 2.0.9 or higher installed in order to compile the examples. Just extract the ZIP file, change the directory into the rsltest folder and run mvn clean install.
Update (2009-09-10): flex-mojos 3.3.0 is released. In case a SWC projects has runtime dependencies of the scope 'rsl' or 'caching' a warning is shown and the scope is automatically changed to 'external'.
Trackback specific URI for this entry