Friday, 6 May 2011

Building a swc with Gradle

After a search for a better build tool, having used Ant & Maven for a couple of years, I stumbled upon Gradle. A build tool that provides the benefits of Ant & Maven combined into one:

  • Dependency Management
  • Better multi-project support
  • Flexibility
Gradle build script are written in Groovy, which allows you to define every step of the build process.
Now, as a Flex developer the first thing I wanted to try out was to build a swc file. I tried to incorporate some multi-project structure into it.
My project structure looks like this:

The settings.gradle file declares which the subprojects are of the mainproject:

include 'subproject'

The build script of the main project contains the configuration needed by every subproject. Gradle provides a "subprojects { ... }" structure for this in which you can place all this code. I'm using maven repositories from which my dependencies will be retrieved:

subprojects {

    //retrieve the FLEX_HOME environment variable
    FLEX_SDK_HOME = System.getenv()['FLEX_HOME']

    configurations {
        compile
        test
    }

    repositories {
        mavenCentral()
        mavenRepo name: 'yoolab-releases',     
                  urls: "http://projects.yoolab.org/maven/content/repositories/releases"
        mavenRepo name: 'yoolab-snapshots',    
                  urls: "http://projects.yoolab.org/maven/content/repositories/snapshots"
    }

    //dependency versions
    def spring_actionscript_core_version = '1.2-SNAPSHOT'
    def as3commons_collections_version = '1.1'
    def as3commons_eventbus_version = '1.1'
    def as3commons_lang_version = '0.3.2'
    def as3commons_logging_version = '2.0'
    def as3commons_reflect_version = '1.3.4'
    def flexunit_version = '0.90'

    dependencies {
        compile group: 'org.springextensions.actionscript',
                name: 'spring-actionscript-core',
                version: spring_actionscript_core_version,
                ext: 'swc'
        compile group: 'org.as3commons',
                name: 'as3commons-collections',
                version: as3commons_collections_version,
                ext: 'swc'
        compile group: 'org.as3commons',
                name: 'as3commons-eventbus',
                version: as3commons_eventbus_version,
                ext: 'swc'
        compile group: 'org.as3commons',
                name: 'as3commons-lang',
                version: as3commons_lang_version, ext: 'swc'
        compile group: 'org.as3commons',
                name: 'as3commons-logging',
                version: as3commons_logging_version,
                ext: 'swc'
        compile group: 'org.as3commons',
                name: 'as3commons-reflect',
                version: as3commons_reflect_version,
                ext: 'swc'

        test group: 'com.adobe.flexunit',
             name: 'flexunit',
             version: flexunit_version,
             ext: 'swc'
    }
}


The build.gradle file of the subproject contains a compile task (don't mistake this with the 'compile' configuration which I declared in the mainproject's build file). This task can be executed by calling the "gradle compile" command from a command line and it will build the swc file.

task compile << {
    println '[compile] Compiling release SWC'
    println '[compile] Using Flex SDK at: ' + FLEX_SDK_HOME

    def projectName = 'subproject'
    def projectVersion = '1.0-SNAPSHOT'

    //initialize compc parameters
    def compiledSwcFileLocation = buildDir.getAbsolutePath() + '/' + projectName + '-' + projectVersion + '.swc'
    def mainSourceLocation = projectDir.getAbsolutePath() + '/src/main/actionscript/'

    ant.java(jar: FLEX_SDK_HOME + '/lib/compc.jar',
             dir: FLEX_SDK_HOME + '/frameworks',
             fork: true,
             resultproperty: 'swcBuildResult',
             errorProperty: 'errorString') {
      arg(value: "-output=" + compiledSwcFileLocation)
      arg(value: "-include-sources=" + mainSourceLocation)
      //add every dependency path
      configurations.compile.files.each{
         dep -> arg(value: "-external-library-path+=" + dep.path)
      }
    }

    //handle failed compile
    if(ant.properties.swcBuildResult == '1') {
       throw new Exception("compc task failed: \n" + ant.properties.errorString);
    }
}


Gradle has some awesome support for Ant tasks (maybe even better then using them in Ant itself). As you can see I'm using the java Ant task to execute the compc compiler, and I'm providing it some ant property names which I can use after the ant task has been finished.

Gradle takes some getting used to (certainly if you've never used Groovy before), but it's totally worth the effort. I'm going to investigate further into this and see how it scales to bigger projects.