Makefile for CSS and JS Minify/Compress

I love Makefiles. Automation in all its forms is desirable, but Makefiles have a combination of surface simplicity and unknowable complexity that I find very endearing. I've had an ever-growing need to automate the minification and compression of CSS and JavaScript files on my web server, and while I'd written some rudimentary Makefiles in the past, I finally found time to write a kick-ass general purpose Makefile that can be applied to any project with very little modification.

You'll want to have the YUI Compressor and Google Closure Compiler installed for minification of CSS and JavaScript, respectively. I installed them to my ~/bin directory, and referenced that directory in my Makefile.

Note that I'm using these two pieces of software mostly out of habit; I haven't evaluated them or their competitors recently.

The Meat

Customizing for Your Project

To make this file useful for your own project, you'll need to point it at your CSS and JS files.

JS_TARGETS =
CSS_TARGETS =
CLEANUP =

The first two variables define what scripts will be minified and compressed when you type "make js" or "make css," respectively. Both of these commands will run when you type "make all" or simply "make." CLEANUP allows you to specify additional files that will be removed when you type "make clean."

Some sample customizations are mentioned in the file.

CSS_TARGETS = $(shell cat manifest.txt)

If you would rather organize your list of CSS or JavaScript targets into their own files, you can automatically expand that manifest file using the cat command.

CLEANUP = $(CSS_TARGETS) $(JS_TARGETS)

Some more advanced setups may combine several CSS files before minification, or use a custom target to concatenate JavaScript using the closure compiler. If you find that all your targets can be cleaned, you can simply reference them automatically as above.

concatenated.min.js: file1.js file2.js
    java -jar ~/bin/compiler.jar $(addprefix --js=,$^) >$@

The closure compiler requires a prefix for all input JavaScript files. Use the make function "addprefix" to format the argument list.

custom-concat.css: file1.css file2.css file3.css
    cat $^ >$@

Automatic variables greatly simplify most of the functionality within this makefile. To create a concatenated CSS file, simplify specify the target to the left of the colon, and its dependencies (the files to concatenate) to the right; the cat command never needs modification.

Of course, you can always define custom rules, ie. this one to fetch the newest development version of jQuery:

jquery:
    curl -o jquery.js http://code.jquery.com/jquery-git.js

Other Magic

Some other goodness happens below the "you shouldn't need to edit past here" line. After we configure a few settings, we define how to create files based on suffixes: .min.css gets passed through YUI Compressor, .min.js through Closure, and .gz through gzip. We configure what files we care about, and make handles the rest.

.DEFAULT_GOAL := all

Set a default rule, rather than using the first rule in the file as the default.

.PHONY: css js

Prevent files named "css" or "js" from interfering with the css and js rules.

%.gz: %
    gzip -9 <$< >$@

One of three pattern rules. This enables you to compress any file (not just CSS or JS) by running "make filename.gz." I precompress my files because I'm using [gzip_static in nginx][].

CSS_GZIP = $(CSS_TARGETS:.css=.css.gz)

One of several variables that modifies filenames found in $(CSS_TARGETS), changing the suffix from .css to .css.gz. Note that the colon/equals syntax is shorthand for patsubst. Given CSS_TARGETS = text.css tables.css, we would get CSS_GZIP = text.css.gz tables.css.gz, which would be sent as targets to the more general css rule, and in turn would be handled by the general-purpose %.gz rule above.

See Also