diff --git a/do b/do new file mode 100644 index 0000000..1a3c693 --- /dev/null +++ b/do @@ -0,0 +1,178 @@ +#!/bin/sh +# +# A minimal alternative to djb redo that doesn't support incremental builds. +# For the full version, visit http://github.com/apenwarr/redo +# +# The author disclaims copyright to this source file and hereby places it in +# the public domain. (2010 12 14) +# + +# By default, no output coloring. +green="" +bold="" +plain="" + +if [ -n "$TERM" -a "$TERM" != "dumb" ] && tty <&2 >/dev/null 2>&1; then + green="$(printf '\033[32m')" + bold="$(printf '\033[1m')" + plain="$(printf '\033[m')" +fi + +_dirsplit() +{ + base=${1##*/} + dir=${1%"$base"} +} + +dirname() +( + _dirsplit "$1" + dir=${dir%/} + echo "${dir:-.}" +) + +_dirsplit "$0" +export REDO=$(cd "${dir:-.}" && echo "$PWD/$base") + +DO_TOP= +if [ -z "$DO_BUILT" ]; then + DO_TOP=1 + [ -n "$*" ] || set all # only toplevel redo has a default target + export DO_BUILT="$PWD"/.do_built + : >>"$DO_BUILT" + echo "Removing previously built files..." >&2 + sort -u "$DO_BUILT" | tee "$DO_BUILT.new" | + while read f; do printf "%s\0%s.did\0" "$f" "$f"; done | + xargs -0 rm -f 2>/dev/null + mv "$DO_BUILT.new" "$DO_BUILT" + DO_PATH="$DO_BUILT".dir + export PATH="$DO_PATH":"$PATH" + rm -rf "$DO_PATH" + mkdir "$DO_PATH" + for d in redo redo-ifchange; do + ln -s "$REDO" "$DO_PATH/$d"; + done + [ -e /bin/true ] && TRUE=/bin/true || TRUE=/usr/bin/true + for d in redo-ifcreate redo-stamp redo-always; do + printf '#!/bin/sh\n' > "$DO_PATH/$d"; + chmod a+x "$DO_PATH/$d"; + done +fi + + +_find_dofile_pwd() +{ + dofile=default."$1".do + while :; do + dofile=default.${dofile#default.*.} + [ -e "$dofile" -o "$dofile" = default.do ] && break + done + ext=${dofile#default} + ext=${ext%.do} + base=${1%$ext} +} + + +_find_dofile() +{ + local prefix= + while :; do + _find_dofile_pwd "$1" + [ -e "$dofile" ] && break + [ "$PWD" = "/" ] && break + target=${PWD##*/}/"$target" + tmp=${PWD##*/}/"$tmp" + prefix=${PWD##*/}/"$prefix" + cd .. + done + base="$prefix""$base" +} + + +_run_dofile() +{ + export DO_DEPTH="$DO_DEPTH " + export REDO_TARGET="$PWD"/"$target" + local line1 + set -e + read line1 <"$PWD/$dofile" || true + cmd=${line1#"#!/"} + if [ "$cmd" != "$line1" ]; then + /$cmd "$PWD/$dofile" "$@" >"$tmp.tmp2" + else + :; . "$PWD/$dofile" >"$tmp.tmp2" + fi +} + + +_do() +{ + local dir="$1" target="$2" tmp="$3" + if [ ! -e "$target" ] || [ -d "$target" -a ! -e "$target.did" ]; then + printf '%sdo %s%s%s%s\n' \ + "$green" "$DO_DEPTH" "$bold" "$dir$target" "$plain" >&2 + echo "$PWD/$target" >>"$DO_BUILT" + dofile="$target".do + base="$target" + ext= + [ -e "$target.do" ] || _find_dofile "$target" + if [ ! -e "$dofile" ]; then + echo "do: $target: no .do file" >&2 + return 1 + fi + [ ! -e "$DO_BUILT" ] || [ ! -d "$(dirname "$target")" ] || + : >>"$target.did" + ( _run_dofile "$target" "$base" "$tmp.tmp" ) + rv="$?" + if [ $rv != 0 ]; then + printf "do: %s%s\n" "$DO_DEPTH" \ + "$dir$target: got exit code $rv" >&2 + rm -f "$tmp.tmp" "$tmp.tmp2" "$DO_BUILT" + return "$rv" + fi + mv "$tmp.tmp" "$target" 2>/dev/null || + ! test -s "$tmp.tmp2" || + mv "$tmp.tmp2" "$target" 2>/dev/null + rm -f "$tmp.tmp2" + else + echo "do $DO_DEPTH$target exists." >&2 + fi +} + + +# Make corrections for directories that don't actually exist yet. +_dir_shovel() +{ + local dir base + xdir="$1" xbase="$2" xbasetmp="$2" + while [ ! -d "$xdir" -a -n "$xdir" ]; do + _dirsplit "${xdir%/}" + xbasetmp=${base}__"$xbase" + xdir="$dir" xbase="$base"/"$xbase" + echo "xbasetmp='$xbasetmp'" >&2 + done +} + + +_redo() +{ + set +e + for i in "$@"; do + _dirsplit "$i" + _dir_shovel "$dir" "$base" + dir="$xdir" base="$xbase" basetmp="$xbasetmp" + ( cd "$dir" && _do "$dir" "$base" "$basetmp" ) + [ "$?" = 0 ] || return 1 + done +} + + +_redo "$@" +[ "$?" = 0 ] || exit 1 + +if [ -n "$DO_TOP" ]; then + echo "Removing stamp files..." >&2 + [ ! -e "$DO_BUILT" ] || + while read f; do printf "%s.did\0" "$f"; done <"$DO_BUILT" | + xargs -0 rm -f 2>/dev/null +fi