Use Ant and Closure Compiler to compress every JavaScript file in a project if you have lots of files

In a previous post, I set out how to use an Ant script to run every JavaScript file in a project through the Closure compiler.

For the most part, this has been working fine for me. Then I ran it against a project with some 50-odd JavaScript files and it started to throw PermGen OutOfMemoryError errors. So, it needed some work.

Iteration

In the first version of the script, I was using Ant Contrib’s foreach task to loop through all JS files in the project then pass these off to another task which would call the compiler. It seems that Ant creates a new classloader as each iteration of the loop calls the compression task. So, as the number of items to be iterated over increases, so does the memory usage.

I got to about 17 files before it failed on me.

For

The Ant Contrib for task solves this by using the same classloader for each iteration, so you don’t get a memory leak.

So, our updated build task looks like this:

<?xml version="1.0"?>
<project basedir="." default="run">

  <taskdef name="jscomp" classname="com.google.javascript.jscomp.ant.CompileTask" classpath="${basedir}/build-lib/compiler.jar"/>
  <taskdef resource="net/sf/antcontrib/antlib.xml">
    <classpath>
      <pathelement location="${basedir}/build-lib/ant-contrib/ant-contrib-1.0b3.jar"/>
    </classpath>
  </taskdef>

  <target name="run">
    <echo message="Searching for files ${jsCompressionWildcard} in ${jsCompressionRootDirectory}." />
    <for param="filename" keepgoing="true">
      <path>
        <fileset dir="${jsCompressionRootDirectory}" casesensitive="yes">
          <include name="${jsCompressionWildcard}"/>
        </fileset>
      </path>
      <sequential>

        <!-- This gets the filename without the directory path. -->
        <basename property="file.@{filename}" file="@{filename}"/>

        <propertyregex property="directory.@{filename}"
                input="@{filename}"
                regexp="^(.+)([^]+)$"
                select="1"
                casesensitive="false" />

        <echo message="Compressing file ${file.@{filename}} in ${directory.@{filename}}" />

        <jscomp compilationLevel="simple" debug="false" output="${directory.@{filename}}${file.@{filename}}" forceRecompile="true">

          <sources dir="${directory.@{filename}}">
            <file name="${file.@{filename}}"/>
          </sources>

        </jscomp>
      </sequential>
    </for>
  </target>

</project>

I’ve also removed the JavaScript code that was stripping the path from the current file and replaced it with a regex.

Update 28/01/2013
It was pointed out to me that there were a couple of issues with the original script. I’ve updated above as follows:

  • I’ve added in the full file path in live 32 as the old script would save the compressed file in its running directory.
  • In the same line, I’ve added the option to force compression of the file. When running using TeamCity, the script was ignoring the JS files without this in place.

Kevin Wilson

.NET developer, JavaScript enthusiast, Android user, Pebble wearer, sometime musician and occasional cook.

6 thoughts on “Use Ant and Closure Compiler to compress every JavaScript file in a project if you have lots of files

  1. Thanks for the post. Did you find any issues with order of the javascript files being loaded ? If so how did you manage that ?

    1. I wasn’t combining any files when I put this together; just compressing each in place. So I didn’t have any issues with the order of the files. You could do something hacky with multiple wildcards but it wouldn’t be pretty.

  2. Hi, I want to compress all the files inside the subdirectory also. I tried the above but doing only for current directory.

  3. Hi,

    nice article but when trying the script in my ant build file it gives an error:

    java.util.regex.PatternSyntaxException: Unclosed character class near index 13
    ^(.+)([^]+)$

    for the regexp:

    regexp=”^(.+)([^]+)$”

    Any ideas ?

Leave a Reply

Your email address will not be published. Required fields are marked *