36

I ran into this useful tip that if you're working on files a lot and you want them to build automatically you run:

watch make

And it re-runs make every couple seconds and things get built.

However ... it seems to swallow all the output all the time. I think it could be smarter - perhaps show a stream of output but suppress Nothing to be done for 'all' so that if nothing is built the output doesn't scroll.

A few shell script approaches come to mind using a loop and grep ... but perhaps something more elegant is out there? Has anyone seen something?

Dobes Vandermeer
  • 8,463
  • 5
  • 43
  • 46
  • `watch make | grep -v "Nothing to be done"`. and maybe you have to redirect stderr. haven't checked – Karoly Horvath Sep 24 '11 at 14:19
  • And/or add `--silent` option, or use hack to suppress 'Nothing to be done for...` message. Neither of these are good reasons for jumping from the frying pan (Unix world) into the fire (Ruby world). –  Oct 19 '14 at 13:08
  • FWIW, there's also [`entr`](https://github.com/clibs/entr), which is a probably a little more versatile. You might invoke it using something like `ag -l | entr make` or `ls -l | entr make`. – Nicolai Weitkemper Jul 05 '21 at 15:22

12 Answers12

61

Using classic gnu make and inotifywait, without interval-based polling:

watch:
    while true; do \
        $(MAKE) $(WATCHMAKE); \
        inotifywait -qre close_write .; \
    done

This way make is triggered on every file write in the current directory tree. You can specify the target by running

make watch WATCHMAKE=foo
Benjamin Philip
  • 178
  • 1
  • 11
Otto Allmendinger
  • 27,448
  • 7
  • 68
  • 79
  • 1
    Thanks for this! Although I rather moved this into a standalone shellscript so that it can be used with any makefile-based project without modifying the makefile. – kralyk Dec 02 '16 at 15:51
  • 1
    Wow, that's beautiful and is very useful for building large LaTeX projects that take about a minute to compile – R Kiselev Dec 08 '17 at 11:37
  • 2
    For macOS, `brew install fswatch` and replace line 4 with `fswatch -1 .` – Shaun Lebron Jan 30 '19 at 15:53
  • 1
    c rookie from node.js, ` `npm i -g nodemon` and `nodemon --watch ./hello.c --exec "gcc ./hello.c && ./a.out"` works for me on MacOS. – JasonHsieh Jun 10 '20 at 08:23
14

Here is a one-liner:

while true; do make -q || make; sleep 0.5; done

Using make -q || make instead of just make will only run the build if there is something to be done and will not output any messages otherwise.

You can add this as a rule to your project's Makefile:

watch:
    while true; do $(MAKE) -q || $(MAKE); sleep 0.5; done

And then use make watch to invoke it.

This technique will prevent Make from filling a terminal with "make: Nothing to be done for TARGET" messages.

It also does not retain a bunch of open file descriptors like some file-watcher solutions, which can lead to ulimit errors.

Chris McCormick
  • 4,356
  • 1
  • 21
  • 19
  • 1
    This solution is the one I'd choose if I could choose the solution. It works in Linux, macOS, gmake and pmake. It's extremely compatible given that it relies on query/question mode which has been around a while (https://www.freebsd.org/cgi/man.cgi?query=make&manpath=FreeBSD+2.0-RELEASE and https://www.freebsd.org/cgi/man.cgi?query=make&manpath=SunOS+4.1.3 both feature it). There are more optimal answers for certain situations but this replaces watch make. – James Andariese Apr 28 '21 at 02:51
13

This one-liner should do it:

while true; do make --silent; sleep 1; done

It'll run make once every second, and it will only print output when it actually does something.

wch
  • 4,069
  • 2
  • 28
  • 36
  • On GNU/Linux the `--silent` flag suppresses all output, including during a build where there are changes. I posted [an alternative one-liner](https://stackoverflow.com/a/48496716/2131094). – Chris McCormick Jan 29 '18 at 07:57
7

How about

# In the makefile:
.PHONY: continuously
continuously:
    while true; do make 1>/dev/null; sleep 3; done  

?

This way you can run

make continuously

and only get output if something is wrong.

erdwolf
  • 71
  • 1
  • 2
  • This doesn't solve the problem, though, which is that I want to see the output of the last "make" that I ran. In fact, this solution discards the output from all invokations of make, so it's worse than "watch make". – Dobes Vandermeer May 20 '13 at 18:26
  • 1
    @DobesVandermeer Replace `make 1>/dev/null` with `make -s` (`-s` for not printing commands) and it will work as desired. – amit kumar Nov 22 '13 at 20:58
6

Twitter Bootstrap uses the watchr ruby gem for this.

https://github.com/twbs/bootstrap/blob/v2.3.2/Makefile

https://github.com/mynyml/watchr

Edit:

After two years the watchr project seems not to be maintained anymore. Please look for another solution among the answers. Personally, if the goal is only to have a better output, i would recommend the answer from wch here

Community
  • 1
  • 1
danza
  • 11,511
  • 8
  • 40
  • 47
2

I do it this way in my Makefile:

watch:
    (while true; do make build.log; sleep 1; done) | grep -v 'make\[1\]'

build.log: ./src/*
    thecompiler | tee build.log

So, it will only build when my source code is newer than my build.log, and the "grep -v" stuff removes some unnecessary make output.

Alfred Godoy
  • 1,045
  • 9
  • 19
2

This shell script uses make itself to detect changes with the -q flag, and then does a full rebuild if and only if there are changes.

#!/bin/sh

while true;
do
  if ! make -q "$@";
  then
    echo "#-> Starting build: `date`"
    make "$@";
    echo "#-> Build complete."
  fi
  sleep 0.5;
done
  • It does not have any dependencies apart from make.

  • You can pass normal make arguments (such as -C mydir) to it as they are passed on to the make command.

  • As requested in the question it is silent if there is nothing to build but does not swallow output when there is.

  • You can keep this script handy as e.g. ~/bin/watch-make to use across multiple projects.

Chris McCormick
  • 4,356
  • 1
  • 21
  • 19
1

There are several automatic build systems that do this and more - basically when you check a change into version control they will make/build - look for Continuous Integration

Simple ones are TeamCity and Hudson

mmmmmm
  • 32,227
  • 27
  • 88
  • 117
  • Hmm that is useful, of course, but in my case I'm thinking of something that builds every few seconds so my changes are processed as soon as I save rather than when I check them into source control. So, not quite what I am looking for. – Dobes Vandermeer Sep 24 '11 at 14:23
1

@Dobes Vandermeer -- I have a script named "mkall" that runs make in every subdirectory. I could assign that script as a cron job to run every five minutes, or one minute, or thirty seconds. Then, to see the output, I'd redirect gcc results (in each individual makefile) to a log in each subdirectory.

Could something like that work for you?

It could be pretty elaborate so as to avoid makes that do nothing. For example, the script could save the modify time of each source file and do the make when that guy changes.

Pete Wilson
  • 8,610
  • 6
  • 39
  • 51
  • Not exactly what I was thinking but it gives me an idea. If I wrapped make in a script that stores the unique outputs from previous makes it could print out the previous outputs each time it runs, and run that script using watch. – Dobes Vandermeer Sep 27 '11 at 14:43
1

You could try using something like inotify-tools. It will let you watch a directory and run a command when a file is changed or saved or any of the other events that inotify can watch for. A simple script that does a watch for save and kicks off a make when a file is saved would probably be useful.

0

Bit of archaeology, but I still find this question useful. Here is a modified version of @otto's answer, using fswatch (for the mac):

TARGET ?= foo

all:
    @fswatch -1 . | read i && make $(TARGET)
    @make -ski TARGET=$(TARGET)

%: %.go
    @go build $<
    @./$@
RoyM
  • 1,118
  • 1
  • 9
  • 28
0

You could change your make file to output a growl (OS X) or notify-send (Linux) notification. For me in Ubuntu, that would show a notification bubble in the upper-right corner of my screen.

Then you'd only notice the build when it fails.

You'd probably want to set watch to only cycle as fast as those notifications can display (so they don't pile up).

idbrii
  • 10,975
  • 5
  • 66
  • 107
  • Were you able to actually do this? notify-send doesn't accept piped input – narthur157 Sep 10 '15 at 13:50
  • 1
    You can use xargs to collect piped output and pass them to a program as arguments. I think when I did this, I used a python API for notify-send. (I had it outputting the result of my nose tests that ran every time I saved. Maybe I just used [nose-notify](https://github.com/passy/nose-notify).) – idbrii Sep 11 '15 at 20:53