Skip to content

Instantly share code, notes, and snippets.

@AdrienHorgnies
Last active July 11, 2024 15:04
Show Gist options
  • Save AdrienHorgnies/699d3e06b3142b1d2ddd3f9bf41272e9 to your computer and use it in GitHub Desktop.
Save AdrienHorgnies/699d3e06b3142b1d2ddd3f9bf41272e9 to your computer and use it in GitHub Desktop.
Continuously make and run an artifact, ignoring files according to git and make.
#!/bin/bash
# --- NEEDLE-HELP-START ---
# NAME
# makew - makew continuously run make upon source files changes.
#
# SYNOPSYS
# makew [TARGET ...] [--cmd <command|file>] [--no-cmd] [--no-git] [--gitignore] [--exclude <regex>] [-h|--help]
#
# DESCRIPTION
# makew calls make with the provided TARGET(s) (or none), and continue doing so whenever a source file changes.
# makew executes the rightmost executable TARGET, and restarts it when make succeeds.
#
# make considers source files changes if:
# - it is not ignored by git
# - at least one target is not up to date according to make
#
# OPTIONS
# --cmd=<command>
# --cmd=<file>
# Execute the provided command or file instead of guessing what to execute.
# --no-cmd
# Only build and don't try to execute anything.
# --no-git
# Consider all files changes, even if they're ignored by git (still ignores the directory .git).
# --gitignore
# Don't even watch ignored files.
# makew watch all files (except .git/*) and then queries git to know which files to ignore upon file system events.
# But a large ignored directory may be slow to watch.
# This option transforms .gitignore into a regex that inotify will respect...
# But it's not possible to implement bang rules (starting with !) with POSIX extended regex.
# makew will miss events on such files... if they were created after its initial start.
# --exclude=<regex>
# Append the POSIX extended regex to inotify --exclude option.
# -h | --help
# Print this help message.
# --- NEEDLE-HELP-END ---
# TODO option --cmd <command>
# TODO option --cmd <file>
# TODO option --no-cmd
# TODO option --no-git
# TODO option --no-git-regex
# TODO option --exclude see inotifywait --exclude
set -e
ARGS=($@)
cmd=
pid=
trap 'trap - ERR SIGINT SIGTERM EXIT; cyan "cleaning up..."; kill -- -$$;' ERR SIGINT SIGTERM EXIT
function cyan() {
echo $(tput setaf 6)$@$(tput sgr0)
}
function magenta() {
echo $(tput setaf 5)$@$(tput sgr0)
}
function red() {
echo $(tput setaf 1)$@$(tput sgr0)
}
function killRunningCommand() {
if [ "$pid" ] && isMyPID "$pid"; then
cyan "kill $cmd ($pid)"
kill -15 "$pid"
fi
pid=
}
# checks this script owns the PID ($1) to avoid killing a random process.
function isMyPID() {
if [ -z "$1" ]; then
return 2
elif [ "$1" -eq 1 ]; then
return 1
elif [ "$1" -eq $$ ]; then
return 0
fi
isMyPID $(ps -o ppid= -p "$1" 2>/dev/null)
}
function makeThenRun() {
cyan "make ${ARGS[@]}"
if make ${ARGS[@]}; then
killRunningCommand
if [ ${#ARGS[@]} -eq 0 ]; then
executable=$(find $(grep -Pom1 '^[[:print:]]+(?=:)' [Mm]akefile) -type f -executable | tail -1)
else
executable=$(find ${ARGS[@]} -type f -executable | tail -1)
fi
if [ -z "$executable" ]; then
return
fi
./$executable &
cmd=$executable
pid=$!
cyan "running $executable ($pid)"
else
red "failed to make"
fi
}
makeThenRun
inotifywait -m -q --exclude './.git/.*' --format '%w%f' -e create -e modify -e delete -r . |
while read filename; do
if ! git check-ignore -q "$filename" && ! make -q ${ARGS[@]}; then
if [ -d "$filename" ]; then
filename="$filename/"
fi
magenta "${filename#*/} changed"
makeThenRun
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment