Last active
July 11, 2024 15:04
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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