3

This feels like a situation where I need to watch a stream (stdin), if a line comes in, wait a moment, then fire the command and wait some more.

Using a tool like pyinotify or fswatch, we can watch a folder for changes and when one is found, echo it out.

fswatch --recursive --latency 2 src/ | xargs make build

or

pyinotify -r -a -e IN_CLOSE_WRITE -c 'make build' src/

In my case, I am trying to figure out how to call make build whenever a file changes. While the above tools do work, they can end up calling make build a lot and in rapid succession. Each tool works a little bit differently, but the end result is the same (make is getting called too much)

I need all of the gyrating to stop, 1 second to elapse, and then invoke make just one time.

Is there some unix way to batch up the commands and then invoke make? Something like this:

fswatch --recursive src/ | aggregate_and_wait --delay 1second | make build

2
  • Write a script that runs the build with a sleep 1 up at the top somewhere. Have your fswatch or pyinotify call that script. Commented Oct 26, 2018 at 15:24
  • If we have 5 lines come in on the stream, this approach will still fire the command 5 times. Commented Oct 26, 2018 at 15:32

1 Answer 1

1

Have your canary (the process looking for changes) write a state file at e. g. /var/run/build-needed.

Set up a cron job to run every minute (or every five minutes, or whatever frequency you think is apt for your use-case) your automated build script that will:

  • check for /var/run/build-needed, and if it is not newer than /var/run/last-build, abort.
  • check for /var/run/build-in-progress and, if it is present, abort.
  • create /var/run/build/in-progress
  • execute the build.
  • remove /var/run/in-progress and touch /var/run/last-build.

An example skeleton implementation:

The canary process:

pyinotify -r -a -e IN_CLOSE_WRITE -c 'touch /var/run/build-needed' src/

The cron job:

*/5 * * * * /path/to/autobuilder.sh

The builder script:

#!/bin/bash
canaryfile="/var/run/build-needed"
lastbuild="/var/run/last-build"
inprogress="/var/run/build-in-progress"
needbuild="no"
if [[ -f "$canaryfile" ]]; then
    if [[ -f "$lastbuild" ]] && [[ "$canaryfile" -nt "$lastbuild" ]]; then
        needbuild="yes"
    elif ! [[ -f "$lastbuild" ]]; then
        needbuild="yes"
    fi
fi
if ! [[ -f "$inprogress" && "yes" == "$needbuild" ]]; then
    cd /path/to/src
    touch "$inprogress"
    if make build; then
        rm "$inprogress"
        touch "$lastbuild"
    fi
fi
3
  • I hate to say it, but this is too complex for what I'm trying to do. I'm actually using a Makefile, so I'm looking for something rather tight, small, minimal. A cron tab entry is too heavy. Overall, however, the notion of a canary file makes sense. How can something like that be done in just 2-3 lines of bash? Commented Oct 26, 2018 at 19:38
  • This pair of commands works, but is there a way to invoke them at the same time and then when I Ctrl+C, they both quit? @fswatch --recursive src/ | xargs -I{} touch build-canary; @while inotifywait -e close_write build-canary; do sleep 3 && make build; done Commented Oct 26, 2018 at 20:03
  • It depends on how robust you want it to be. This example for instance also makes sure you don't start one build while another is going, which is a good idea, but not one mentioned in your original question. Commented Oct 26, 2018 at 20:03

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.