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.
Thanks for the post. Did you find any issues with order of the javascript files being loaded ? If so how did you manage that ?
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.
Hi, I want to compress all the files inside the subdirectory also. I tried the above but doing only for current directory.
The script should compress everything that matches the wildcard. There’s some sample code here if you want to try it – https://github.com/kwilson/google-closure-ant-compiler/blob/master/build.xml
Wildcard I was using was **/*.js
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 ?
Hi Stefan,
I got the same error, it worked for me after the following change