Rebuild code automatically with "watch" and a makefile
March 31, 2013
While poking around some random projects by the prolific TJ Holowaychuk, I came upon his version of watch, which repeats a command endlessly. You run
$ watch some_command_here
and it runs that command over and over, on an interval. By default, once per second.
The idea is to pair it with
make to run tests and automatically rebuild/recompile code. Since
make tracks dependencies and looks at file modification times, it won’t do anything unless the relevant source files have changed.
There’s an older program called
watch that comes with Linux, but it doesn’t play as nicely with
make. In particular, it clears the screen every time the command is run. For pairing this with
make, we want new output to be printed whenever
make actually does anything, and otherwise we want it to be silent.
To avoid any confusion I patched TJ’s version of the program, changing its binary name to
tjwatch. Now if we run
tjwatch make, make will be run once per second.
There’s a small problem though.
make prints out this status message if it doesn’t have anything to do:
$ make make: Nothing to be done for `all'.
So by running it once a second we get flooded with the message over and over again.
$ tjwatch make make: Nothing to be done for `all'. make: Nothing to be done for `all'. make: Nothing to be done for `all'. ^C
We don’t want this. We do want output when make actually does something:
$ touch script/app/views.js $ make browserify -t hbsfy script/main.js -o static/bundle.js
make prints out the command it executed.
GNU make doesn’t have an option to suppress the “Nothing to be done” message, but still show other output. One idea would be to use
$ tjwatch "make | grep -v 'Nothing to be done for'" exit: 1 exit: 1 exit: 1 ^C
But now we keep getting warned by
tjwatch about the non-zero exit code, which is the exit code of
grep, not of
A Stack Overflow answer suggests a better workaround. We can make an “all” task in the Makefile that does nothing, yet makes
make think it’s doing something, by changing this:
.PHONY: all realAll all: realAll @true realAll: static/bundle.js
The true(1) command does nothing, and the
@ prevents the command itself from being echoed.
tjwatch make is silent while idle, but prints commands and output when it actually does anything. Perfect.