Tag: ant

 

jslint4maven

A real maven plugin for jslint4java would be nice. I will write one, but until then, you can always get away with using the antrun plugin. This is fairly simple to do now that jslint4java is available in the central maven repository.

Here’s a plugin definition of how to do it.

<project>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.3</version>
        <dependencies>
          <dependency>
            <groupId>com.googlecode.jslint4java</groupId>
            <artifactId>jslint4java-ant</artifactId>
            <version>1.3</version>
          </dependency>
        </dependencies>
        <executions>
          <execution>
            <id>jslint</id>
            <phase>test</phase>
            <goals>
              <goal>run</goal>
            </goals>
            <configuration>
              <tasks>
                <ant antfile="${basedir}/jslint.xml">
                  <property name="root" location="${basedir}" />
                  <target name="jslint" />
                </ant>
              </tasks>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

Notice how you can add the jslint4java-ant dependency to the antrun plugin without affecting the dependencies of the project.

At the test phase, maven will now run jslint for you. This relies on a small external ant build file to actually perform the jslint task.

<!-- jslint.xml -->
<project xmlns:jsl="antlib:com.googlecode.jslint4java">
  <target name="jslint">
    <jsl:jslint>
      <formatter type="plain" />
      <fileset dir="${root}/src/main/webapp" includes="**/*.js" />
    </jsl:jslint>
  </target>
</project>

We can use the nice antlibs namespace style of declaration because the jslint4java jar is in the classpath already.

Here, I’m assuming that we’re in a war project and want to validate all the files under src/main/webapp. If this goes wrong, you’ll get an error and the build will stop. For example:

…
Tests run: 26, Failures: 0, Errors: 0, Skipped: 0
 
[INFO] [antrun:run {execution: jslint}]
[INFO] Executing tasks
 
jslint:
[jsl:jslint] contact.js:1:8:Bad line breaking before '+'.
[jsl:jslint]         + " Nulla in felis Aliquam luctus Proin tincidunt nisi Donec suscipit"
[jsl:jslint]         ^
…
[jsl:jslint] load.js:17:6:Missing semicolon.
[jsl:jslint]     })
[jsl:jslint]       ^
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] An Ant BuildException has occured: The following error occurred while executing this line:
/Users/dom/work/someproj/jslint.xml:4: 3 files did not pass JSLint

One side note: originally, I tried to fold the jslint.xml file into the POM as well. But this failed to load the antlib. I suspect that xmlns attributes were not being passed in to the tasks definition. It’s not a big deal..

AntUnit inside Maven

I’m in the middle of converting jslint4java to use maven as its build system (yes, really). Part of this is ensuring that the antunit tests I wrote continue to work. Maven has the antrun plugin, but it’s not 100% obvious how to use an antlib inside it.

Normally, to run an antlib extension as part of your build, you first dump the jar in ~/.ant/lib (or on the command line using a -lib flag) and then reference it in a namespace. e.g.

  <project name="foo"
           xmlns:au="antlib:org.apache.ant.antunit"></project>

This relies on ant being able to pick out org/apache/ant/antunit/antlib.xml from the classpath.

When doing this in maven through the antrun plugin, you can’t just dump stuff into ~/.ant/lib however. You need to tell ant where everything is. Originally, I was following the example of the rat ant task. This attempts to invoke a second copy of ant with the correct -lib argument. It’s ugly though. Why should we fork a second copy of the JVM? And ant may not even be installed (in maven, it’s just jar files in the local repository).

Eventually, I looked closer at the antlib documentation and found Load antlib from inside of the buildfile. This shows how a typedef is the key: You can associate a given antlib URI with a particular classpath entry. Thankfully, maven-antrun-plugin provides ${maven.test.classpath} (amongst others) which contains every entry that we need. So the solution now looks something like this.

Firstly, pom.xml.

  <project>
    <dependencies>
      <dependency>
        <groupId>org.apache.ant</groupId>
        <artifactId>ant-antunit</artifactId>
        <version>1.0</version>
        <scope>test</scope>
      </dependency>
    </dependencies>
    <build>
      <plugin>
        <artifactId>maven-antrun-plugin</artifactId>
        <executions>
          <execution>
            <phase>test</phase>
            <goals>
              <goal>run</goal>
            </goals>
            <configuration>
              <tasks>
                <ant antfile="${build.testOutputDirectory}/antunit/tests.xml"
                  inheritAll="false" inheritRefs="false">
                  <property name="test.classpath" refid="maven.test.classpath" />
                </ant>
              </tasks>
            </configuration>
          </execution>
        </executions>
        <dependencies>
          <!-- Force an upgrade to a newer ant version.  Required by antunit. -->
          <dependency>
            <groupId>org.apache.ant</groupId>
            <artifactId>ant</artifactId>
            <version>1.7.0</version>
          </dependency>
        </dependencies>
      </plugin>
    </build>
  </project>

So, when we get to the test phase, we automatically run antunit/tests.xml, passing in the correct classpath. This is what the ant file looks like:

  <project name="jslint-antunit"
           xmlns:au="antlib:org.apache.ant.antunit">
 
      <taskdef uri="antlib:org.apache.ant.antunit"
               resource="org/apache/ant/antunit/antlib.xml"
               classpath="${test.classpath}" /></project>

This appears to work quite well, and runs my tests a sight quicker than before. If anybody from the apache rat project is listening, you might want to update your POMs. 🙂

jslint4java 1.2

I’ve finally gotten around to finishing off the code that I’ve had sitting around in google code for over a year, and released jslint4java 1.2. The changes are actually pretty small:

  • Update to jslint 2008-09-04. This adds several new options.
  • Several updates to the ant task:
    • Move the antlib definition to “antlib:net.happygiraffe.jslint” (incompatible change).
    • Default to failing the build if validation fails (incompatible change).
    • Allow use of the fileset element to be more flexible about specifying where your javascript files are. This replaces several attributes on the jslint task (incompatible change).
    • Add a formatter sub-element, which can output either XML or plain text, either to the console or to a file.
    • See the documentation for some examples of the new usage.
  • Allow access to the HTML report produced by JSLint through an API.

I’ve also uploaded the javadoc directly into subversion on google code, so it’s permanently online. The google-collections guys seem to do this, so it can’t be a bad idea, right?

Typical. Five minutes after I release, I notice that it’s been compiled with 1.6 instead of 1.5. So, I’ve just released v1.2.1…

One-Jar

I was trying to fix the build of a project last night. It used to unpack several dependent jar files before repackaging them along with the project code. How ugly. Then I remembered about one-jar, which lets you package things in a slightly more sensible manner, retaining the individual files.

The documentation isn’t as good as it could be. But after a bit of poking around, I came up with this ant task to build my jar.

  <property name="onejar" value="one-jar-boot-0.95.jar"/>

    <target name="dist" depends="compile">
        <!-- Set up the on-jar directory structure. -->
        <mkdir dir="${build.onejar.dir}" />
        <mkdir dir="${build.onejar.dir}/boot" />
        <mkdir dir="${build.onejar.dir}/lib" />
        <mkdir dir="${build.onejar.dir}/main" />
        <!-- First, build just our code into a jar of its own. -->
        <jar destfile="${build.onejar.dir}/main/main.jar"
             basedir="${build.prod.dir}">
            <manifest>
                <attribute name="Main-Class"
                           value="net.happygiraffe.Main" />
            </manifest>
        </jar>
        <!-- Next, copy in the libs we depend upon. -->
        <copy todir="${build.onejar.dir}/lib">
            <fileset dir="${vendor.lib.dir}">
                <include name="*.jar" />
                <exclude name="${onejar}" />
            </fileset>
        </copy>
        <!-- Extract the One-Jar bits on top. -->
        <unjar src="${vendor.lib.dir}/${onejar}"
               dest="${build.onejar.dir}" />
        <!-- And finally, repackage. -->
        <jar destfile="${jar.path}"
             basedir="${build.onejar.dir}"
             manifest="${build.onejar.dir}/META-INF/MANIFEST.MF" />
    </target>

And then when I tried to run the jar, it fell over.

  Exception in thread "main" java.util.zip.ZipException: No such file or directory
          at java.util.zip.ZipFile.open(Native Method)
          at java.util.zip.ZipFile.<init>(ZipFile.java:203)
          at java.util.jar.JarFile.<init>(JarFile.java:132)
          at java.util.jar.JarFile.<init>(JarFile.java:70)
          at com.simontuffs.onejar.Boot.run(Boot.java:179)
          at com.simontuffs.onejar.Boot.main(Boot.java:105)

After substantial amounts of time messing around, it turned out that it’s a buug when you’re using Mac OS X and Java 5. The latest CVS code has a fix (Boot.java 1.14). Once I’d got the latest version from CVS, everything worked wonderfully. I only hope that it’s released soon.

Not only that, but when I looked through the CVS code, I noticed that there was an ant task as well. That would certainly simplify the rather verbose script above. I’ll have to look into that.

If you’re interested in the architecture of one-jar, check out the developerworks article Simplify your application delivery with One-JAR.

Ant & JUnit

I’ve just been tripped up by ant & JUnit. I’m appalled. All the proposed solutions are both hackish and ugly. I don’t actually care about the intricacies of ClassLoaders. I just want a simple way to tell ant “junit is over here” so that my build and tests will work on whatever system I check out on. I don’t want to have to install stuff into the ant directory on each system. I already have junit.jar in my codebase, and it should be able to use it.

I suppose I could get around it by writing a build.sh which sets the CLASSPATH correctly, but I thought ant was supposed to avoid all that?