Tuesday, August 3, 2010

Using Google's Closure Compiler with GNU Make

I came into the unix scene a bit late and have never really used GNU Make for anything beyond compiling a few C files. After replacing an ad-hoc python-based build system with something based on Make, though, it has been growing on me.

Here is the part of the build script used to integrate with Google's Closure Compiler, in the hopes that it might be useful. This is a long way from being general, but some bits of it might be helpful. It creates one main output file, js.js, from a bunch of libraries and any code needed from the closure library. There is a simple compilation pass and an advanced compilation pass because I use some libraries that don't work with the advanced compilation. A source map is also created.

I read Recursive Make Considered Harmful, so the file below is called js.mk which is imported into the main Makefile with:

include makefiles/js.mk

Here is js.mk:

CLOSURE_COMPILER = java -jar ~/opt/compiler.jar
CALC_DEPS = std_root/dev_only/closure/bin/calcdeps.py

JS_ADVANCED = <js files which can be compiled with advanced compilation>
JS_SIMPLE = <js files which must be compiled with simple compilation>
JS_EXTERNS = <js files containing "externs" (see closure compiler documentation)>

# Modify me to point to where your JS files are located.
vpath %.js std_root/static/:std_root/dev_only:js_extern/

# Concatenate the simple and advanced output together
js.js: simple.js advanced.js
 cat $^ > $@
 mv js.js std_root/static/

# I'm not 100% happy with the way externs are set up here, but it works! Huzzah!
advanced.js: $(JS_ADVANCED) $(JS_EXTERNS)
 $(CLOSURE_COMPILER) --compilation_level=ADVANCED_OPTIMIZATIONS --create_source_map ../../js-source-map \
  $(addprefix --js=, $(filter-out js_extern%, $^)) \
  $(addprefix --externs=, $(filter js_extern%, $^)) \
  --js_output_file=$@

# Note about simple optimizations: Unless you are going to include jquery in the
# compilation, nothing related to jquery should go in either since the symbols
# will need to be fixed in each file manually... which is just way too much work.
# (of course if no weird symbols are used you can go ahead and put it in)
simple.js: $(JS_SIMPLE)
 $(CLOSURE_COMPILER) --compilation_level=SIMPLE_OPTIMIZATIONS \
  $(addprefix --js , $^) \
  --js_output_file=$@

# Inserts dependencies from closure library
cat.js: cleanse.js
 $(CALC_DEPS) -i $^ -o script > $@

# Remove any lines containing a console statement
cleanse.js: main.js
 cat $^ | grep -v "console." > $@

# nothing to do for normal js files
# note: overriden for -soy.js
%.js : ;

# Targets which don't need to be saved can go in here
.INTERMEDIATE: cleanse.js

.PHONY: clean-js
clean-js:
 rm -f advanced.js
 rm -f cat.js
 rm -f std_root/static/js.js
 rm -f simple.js

Be the first to reply!

Post a Comment

By submitting a comment you assert that it is your own original work and agree to grant a non-exclusive licence to Brandon Thomson to display it on log.bthomson.com.