shake

minimal build system that generates Ninja build files

git clone https://9o.is/git/shake.git

commit 6037e5a59dd80820ff649e5adca0cc694a810d83
parent 8f640e3e3247fb4a7c33634d15d35191abd9e435
Author: Jul <jul@9o.is>
Date:   Sun, 15 Mar 2026 21:04:48 +0800

save inputs/outputs in fd 4/5 and ninja in fd 3

Diffstat:
Mshake | 145++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
1 file changed, 84 insertions(+), 61 deletions(-)

diff --git a/shake b/shake @@ -5,11 +5,11 @@ SHAKE_BIN="$0" TARGET= TARGET_ROUTE= SHAKEDIR=./.shake +SHAKEFILE=$SHAKEDIR/local DIR=. OUTDIR=. NINJA_FILES=$SHAKEDIR/local.ninja GEN_FILES="./Shakefile $SHAKE_BIN" -GEN_OUTS=$SHAKEDIR/local.ninja usage() { printf "usage: shake [options] [directory] @@ -50,12 +50,6 @@ while [ $# -gt 0 ]; do esac done -fini_gen() { - gen $GEN_OUTS: - $GEN_FILES - phony ninja: $NINJA_FILES - wait -} - import() { GEN_FILES="$GEN_FILES $DIR/$1" . $DIR/$1 @@ -71,79 +65,112 @@ in_target_route() { [ ! "$TARGET" ] || has $DIR/$1 $TARGET_ROUTE } -_shake_out() { - _f=$3 - if [ ! "$TARGET" ] || [ $TARGET = "$1" ]; then - printf '' >$_f.tmp - $(type -P cat) >$_f.tmp - if cmp -s $_f.tmp $_f; then - rm -f $_f.tmp +_shake_open() { + _f=$SHAKEFILE + + if [ ! "$TARGET" ] || [ $TARGET = "$DIR" ]; then + mkdir -p $SHAKEDIR + exec 3> $_f.ninja.tmp + exec 4> $_f.ins.tmp + exec 5> $_f.out.tmp + else + exec 3> /dev/null + exec 4> /dev/null + exec 5> /dev/null + fi +} + +_shake_close() { + _f=$SHAKEFILE + + gen $_f.ninja $_f.ins $_f.out: - $GEN_FILES + phony ninja: $NINJA_FILES + + exec 3>&- + exec 4>&- + exec 5>&- + + wait + if [ ! "$TARGET" ] || [ $TARGET = "$DIR" ]; then + if cmp -s $_f.ninja.tmp $_f.ninja; then + rm -f $_f.ninja.tmp else - mv $_f.tmp $_f + mv $_f.ninja.tmp $_f.ninja fi - else - $(type -P cat) >/dev/null + sort -u $_f.ins.tmp -o $_f.ins + sort -u $_f.out.tmp -o $_f.out + rm -f $_f.ins.tmp + rm -f $_f.out.tmp fi } sub() { - printf 'subninja %s/%s.ninja\n' "$SHAKEDIR" "$1" - { - [ "${2-}" ] && eval "$2 $1" - $1 - [ "${3-}" ] && eval "$3 $1" - } | _shake_out "$DIR" "$OUTDIR" "$SHAKEDIR/$1.ninja" + printf 'subninja %s/%s.ninja\n' "$SHAKEDIR" "$1" >&3 + SHAKEFILE=$SHAKEDIR/local + _shake_open + [ "${2-}" ] && eval "$2 $1" + $1 + [ "${3-}" ] && eval "$3 $1" + _shake_close } shake() { - printf 'subninja %s/%s/local.ninja\n' "$SHAKEDIR" "$1" + printf 'subninja %s/%s/local.ninja\n' "$SHAKEDIR" "$1" >&3 NINJA_FILES="$NINJA_FILES $dir/$1/ninja" if in_target_route $1; then - mkdir -p $SHAKEDIR/$1 { DIR=$DIR/$1 OUTDIR=$OUTDIR/$1 SHAKEDIR=$SHAKEDIR/$1 + SHAKEFILE=$SHAKEDIR/local GEN_FILES="$GEN_FILES $DIR/Shakefile" - GEN_OUTS=$SHAKEDIR/local.ninja NINJA_FILES=$SHAKEDIR/local.ninja + _shake_open var dir $dir/$1 var outdir $outdir/$1 [ "${2-}" ] && eval "$2 $1" . $DIR/Shakefile [ "${2-}" ] && eval "$3 $1" - fini_gen - } | _shake_out "$DIR/$1" "$OUTDIR/$1" "$SHAKEDIR/$1/local.ninja" & + _shake_close + } & fi } var() { - printf '%s =' "$1" + printf '%s =' "$1" >&3 for _v in ${*:2}; do - printf ' %s' "$_v" + printf ' %s' "$_v" >&3 done - printf '\n' + printf '\n' >&3 eval "$1='\$$1'" } bind() { - printf ' %s =' "$1" + printf ' %s =' "$1" >&3 for _v in ${*:2}; do - printf ' %s' "$_v" + printf ' %s' "$_v" >&3 done - printf '\n' + printf '\n' >&3 } _shake_prefix() { case "$2" in '$'*|'./'*|'../'*|'/'*) - printf ' %s' "$2" + printf ' %s' "$2" >&3 + case "$1" in + $dir) printf '%s\n' "$2" >&4;; + $outdir) printf '%s\n' "$2" >&5;; + esac ;; *) - printf ' %s/%s' "$1" "$2" + printf ' %s/%s' "$1" "$2" >&3 + case "$1" in + $dir) printf '$dir/%s\n' "$2" >&4;; + $outdir) printf '$outdir/%s\n' "$2" >&5;; + esac ;; esac } @@ -154,10 +181,10 @@ _shake_build() { fi case "$1" in - -) printf ' |';; - --) printf ' ||';; + -) printf ' |' >&3;; + --) printf ' ||' >&3;; :) - printf ': %s' $_brule + printf ': %s' $_brule >&3 _pre=$dir ;; esac @@ -171,7 +198,7 @@ build() { _brule=$1 _pre=$outdir - printf 'build' + printf 'build' >&3 for _v in ${*:2}; do case "$_v" in *:*) _shake_build : "$_v";; @@ -180,21 +207,21 @@ build() { *) _shake_prefix $_pre "$_v";; esac done - printf '\n' + printf '\n' >&3 } rule() { - printf 'rule %s\n' "$1" + printf 'rule %s\n' "$1" >&3 bind command "${*:2}" eval "$1() { build $1 \$*; }" } default() { - printf 'default' + printf 'default' >&3 for _v in $*; do _shake_prefix $dir "$_v" done - printf '\n' + printf '\n' >&3 } phony() { @@ -266,26 +293,22 @@ if [ "$TARGET" ]; then set_target_route $TARGET fi -mkdir -p $SHAKEDIR - -{ - var ninja_required_version 1.8 - var builddir $SHAKEDIR - var dir $DIR - var outdir $OUTDIR - - rule gen "$SHAKE_BIN $dir" - bind description SHAKE $dir - bind generator 1 - bind restat 1 +_shake_open +var ninja_required_version 1.8 +var builddir $SHAKEDIR +var dir $DIR +var outdir $OUTDIR - trap fini_gen EXIT +rule gen "$SHAKE_BIN $dir" +bind description SHAKE $dir +bind generator 1 +bind restat 1 - . $DIR/Shakefile +. $DIR/Shakefile - phony build.ninja: ninja - bind generator 1 -} | _shake_out "$DIR" "$OUTDIR" "$SHAKEDIR/local.ninja" +phony build.ninja: ninja +bind generator 1 +_shake_close if [ ! -L build.ninja ]; then rm -f build.ninja