Note: I edited this and turned it into a blog post - read that instead.
While poking around some random projects by the prolific TJ Holowaychuk, I came upon his version of watch, which simply executes a given command periodically.
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 if the relevant source files haven't changed.
There's a 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 example.lua
$ make
lua2js example.lua example.js
In this example, Lua code is being compiled to JavaScript. Note how it prints out the command that was 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 grep
:
$ 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 make
.
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:
all: example.js stuff.js
to this:
.PHONY: all realAll
all: realAll
@true
realAll: example.js stuff.js
@true
runs the true(1) command without echoing the command (that's what the @
means).
Now tjwatch make
is silent while idle, but prints commands and output when it actually does anything. Perfect.