Introducing Grunt
weblog.bocoup.com
weblog.bocoup.com
He says that maintaining a monolithic Makefile doesn't scale, but that seems unlikely considering how far BSD stretches Makefiles for it's ports system. Sure, Make has some rough edges, but I don't see how Javascript is possibly preferable for gluing together build processes.
The fundamental problem here is that "task based build system" is a silly idea. Make isn't a programming language, you don't define function names. Make is a dependency graph with files for nodes and command lines for edges. We already have a system for "tasks": stick things in ./script or add things to PATH!
By all means, make a metapackage for your test, lint, etc scripts, but define your concatonation like this:
out/foo.js: banner src/foo.js
cat $^ > $@
And generalize your minification using a pattern rule, here's ours: %.min.js: %.js
$(yui) --type js --nomunge < $< > $@
Then add in a gzipping rule: %.gz: %
gzip --best < $< > $@
You could trivially stick that sort of thing into `grunt.mk`...And here's how you'd use that:
include grunt.mk
my-app.min.js.gz: my-app.min.js
my-app.min.js: $(find src -name '*.js')
That said, sometimes it is nice to add some "tasks" for discoverability & encouraging their usage: include grunt.mk
.PHONY: all lint test
all: my-app.min.js.gz lint test
my-app.min.js.gz: my-app.min.js
my-app.min.js: $(find src -name '*.js')
test:
./test/run
lint:
./script/lint src
What if you want to run a subset of tests? Or lint with particular flags? Run a shell command like this one: $ grep test: -A1 Makefile
Then copy paste and edit the output!Simpler is better.
Here's what it takes to make a new Javascript project:
mkdir project
cd project
touch foo.js bar.js
echo -e 'project.js: foo.js bar.js\n\tcat $^ > $@' > Makefile
Unless you're jQuery, you don't need minification or GZipping because clients of your library already have their own. You don't need your own linting because you can trivially run `jshint project.js`.If you find yourself running the same command in your shell a bunch of times, add it to your Makefile. It's that simple.
Also, based on your comments, I think you and I have different definitions of "simple". :)
You might be using it to mean what he's defined as "easy".
for the rule:
%.gz: %
wouldn't you need something like patsubst? Regardless of your response, could you share more info?edit, that rule above only makes sense to me if the file name to be gzipped was passed to make. Is that correct? (I'm new to make)
0 0 0 transparent,
or it will mangle the webfonts. I don't know why they still haven't fixed this.However, at Mozilla's PDF.js we needed more flexibility - there were so many JS packaging tools out there, each shining for specific purposes, but none general enough for our needs.
So I wrote a port of Unix shell commands (including a Make-like tool) for Node.js which works across platforms:
http://github.com/arturadib/shelljs
The downside is that we don't offer Grunt tools out-of-the-box; the upside is that the tool is more general and (if you already speak Unix shell) you don't have to learn a new framework.
Again, superb work!
Example: I'm using Coffeescript/Closure/whatever as well as LESS/SASS/Stylus as well as some javascript package system. Whenever I change any of those files, I want the appropriate compiler to get run. I might want to then cat all of the output files into a single big file (e.g. main.css). Now I can edit-refresh in peace.
But now I'm done developing, I want to test and deploy. Now I really want that minification. Perhaps I want require.js's optimzation binary to run. Anything that wasn't cat'ed together should get cat'ed now.
Sure, you can make this two separate tools, but I'd rather have just one.
You could easily create a script that ran "node myfile.js" every time you saved.
And you could easily have a set of "dev" watch tasks configured, along with "deploy" tasks for minification, etc. There's a lot there, and if you want more just file an issue and we can see what makes sense to implement.
The problem with pretty much all build/deploy tools is that they are "just one". There are different tasks, and they _should_ be split.
* Monitoring a filesystem for changes. * Traversing a dependency graph, and computing the minimal set of tasks from there * Execute arbitrary user actions for each task. (For bonus points, allow a feedback channel from actions to DAG) * For extra bonus points, monitor your actions on the OS level to update the DAG automatically. It's neat gcc can create a list of includes. It'd be much neater if I automatically could get a list of dependencies for any tool I run.
Making them into one monolithic tool means inevitably there are some cases the tool is less-than-useful.
It runs a static file server that transparently compiles resources as they are requested (so you request main.js and it finds main.coffee, compiles it, and sends it back).
Then when you're ready to deploy, it's one command to compile/concatenate/minify your css and javascript.
Right now it only supports Coffescript, SASS and HAML, but adding additional compilers is really simple.
It can be used during development, as a runtime or buildtime solution. Also it can be used as a command line tool. It supports almost all known processors (less, sass, coffee, etc) and allows static code analysis with jshint, jslint or csslint.
Personally I say big ups and thank you.