Introducing GwtCompilerTask

One of the first things I did when I started working with GWT was to figure out how to compile an application. I don't mean running the myApp-compile.[cmd|sh] that the setup utilities create. I mean ripping apart those compile scripts and writing an Ant script to do it. Here's how in a nutshell:

<target name="gwtc">
  <java 
      classname="com.google.gwt.dev.GWTCompiler" 
      classpathref="classpath.gwt" 
      fork="true">
    <arg value="com.foo.gwt.myapp.MyApp" />
  </java>
</target>

But this past weekend, I found I needed something a little more powerful. I was writing a build target to compile all of the demos in the GWT Incubator. I wanted to avoid having to explicitly list all of the demo modules and use a <java> task call for each one. The best way to find demo modules seemed to be starting from the demo source root directory and scanning for .gwt.xml files. Ant has a <foreach> task that will do just that. The problem then was that <foreach> identifies the module files (com/foo/gwt/myapp/MyApp.gwt.xml), but GWTCompiler requires the logical module name (com.foo.gwt.myapp.MyApp). Ant is great for manipulating files, but it doesn't have much to offer in the way of runtime String manipulation. The solution? Leverage a more full-bodied programming environment. Like Java.

So I wrote a custom Ant task to handle invoking the GWTCompiler and to bridge the gap between the module's file name and it's logical name. Here's what the demo build looks like:

<target name="gwtc" depends="compile">
  <!-- define the GwtCompilerTask -->
  <taskdef 
      name="gwtc" 
      classname="com.google.ant.GwtCompilerTask">
    <classpath>
      <path path="${project.bin}" />
      <pathelement location="${gwt.dev.jar}" />
    </classpath>
  </taskdef>

  <property name="gwtc.vm.maxMemory" value="512m" />

  <!-- gwtc supports compiling with moduleName or moduleFile.
       Use of moduleFile requires setting src so that the 
       logical module name can be determined by comparing the
       moduleFile path to the source root path. vmMaxMemory
       sets the -Xmx VM argument. -->
  <gwtc src="${gwtc.src.dir}"
        out="${gwtc.out.dir}"
        moduleFile="${gwtc.module.file}"
        style="${gwtc.js.style}"
        vmMaxMemory="${gwtc.vm.maxMemory}">
    <!-- gwtc supports nested classpath -->
    <classpath>
      <path path="${gwtc.src.dir}" />
      <path path="${project.src}" />
      <path path="${project.bin}" />
      <pathelement location="${gwt.user.jar}" />
      <pathelement location="${gwt.dev.jar}" />
      <pathelement 
        location="${gwt.tools}/lib/w3c/sac/sac-1.3.jar" />
      <pathelement 
        location="${gwt.tools}/lib/w3c/flute/flute-1.3.jar" />
    </classpath>
  </gwtc>
</target>

<target name="build.demos">
  <property 
      name="demo.src.dir"
      value="${project.root}/src-demo" />
  <property name="demo.out.dir" value="${project.root}/demo" />
  <property name="demo.js.style" value="PRETTY" />

  <!-- Scan for any file under src-demo ending in .gwt.xml. For
       each file, invoke the gwtc target. The full path to the
       file is passed to gwtc target as gwtc.module.file -->
  <foreach target="gwtc" param="gwtc.module.file">
    <param name="gwtc.src.dir" value="${demo.src.dir}" />
    <param name="gwtc.out.dir" value="${demo.out.dir}" />
    <param name="gwtc.js.style" value="${demo.js.style}" />
    <path>
      <fileset dir="src-demo">
        <include name="**/*.gwt.xml" />
      </fileset>
    </path>
  </foreach>
</target>

GwtCompilerTask can also be used for building regular single-module GWT applications. GwtCompilerTask is available in the GWT Incubator. You will need to build gwt-incubator.jar from trunk at revision 1133 or later.

No comments:

Post a Comment