shake

minimal build system that generates Ninja build files

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

commit f19972f48ea4f15efcf75f6b7d1c0e3efad1908c
parent dd43fa80e36771405e7801f87d83a778f90f3036
Author: Jul <jul@9o.is>
Date:   Sun, 15 Mar 2026 14:53:03 +0800

migrat awk script logic to shell

Diffstat:
Mexample/simple/Shakefile | 2+-
Minstall | 11+----------
Mshake | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Dshakeout.awk | 124-------------------------------------------------------------------------------
4 files changed, 71 insertions(+), 162 deletions(-)

diff --git a/example/simple/Shakefile b/example/simple/Shakefile @@ -17,6 +17,6 @@ cc hello.o: hello.c link hello: main.o hello.o cp $DESTDIR$PREFIX/bin/hello: hello -phony install $DESTDIR$PREFIX/bin/hello +phony install: $DESTDIR$PREFIX/bin/hello default hello diff --git a/install b/install @@ -4,13 +4,11 @@ set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" DESTDIR="${DESTDIR-}" PREFIX="${PREFIX:-/usr/local}" -SHAKE_LIB="/share/shake" usage() { printf "usage: install.sh [options] --prefix=DIR installation prefix (default: /usr/local) --destdir=DIR staging directory (for packaging) - --shakelib=DIR path to shake lib (default: $SHAKE_LIB) --help show this help " >&2 exit 1 @@ -27,10 +25,6 @@ while [ $# -gt 0 ]; do DESTDIR="${1#*=}" shift ;; - --shakelib=*) - SHAKE_LIB="${1#*=}" - shift - ;; --help|-h) usage ;; @@ -44,8 +38,5 @@ done set -x mkdir -p "${DESTDIR}${PREFIX}/bin" -mkdir -p "${DESTDIR}${PREFIX}${SHAKE_LIB}" -cp -f shakeout.awk "${DESTDIR}${PREFIX}${SHAKE_LIB}" -sed "s|%%SHAKE_LIB%%|${DESTDIR}${PREFIX}${SHAKE_LIB}|" \ - "$SCRIPT_DIR/shake" > "${DESTDIR}${PREFIX}/bin/shake" +cp -f "$SCRIPT_DIR/shake" "${DESTDIR}${PREFIX}/bin/shake" chmod 0755 "${DESTDIR}${PREFIX}/bin/shake" diff --git a/shake b/shake @@ -2,7 +2,6 @@ set -eu SHAKE_BIN="$0" -SHAKE_LIB="%%SHAKE_LIB%%" TARGET= TARGET_ROUTE= BUILDDIR=.shake @@ -15,7 +14,6 @@ GEN_OUTS=$DIR/$NINJA_FN.ninja usage() { printf "usage: shake [options] [directory] - -l dir override shake lib path (default: $SHAKE_LIB) -C dir change to dir before doing anything -o dir set the output directory -h show this help @@ -25,11 +23,6 @@ usage() { while [ $# -gt 0 ]; do case "$1" in - -l) - [ $# -lt 2 ] && usage - SHAKE_LIB="${2%/}" - shift 2 - ;; -o) [ $# -lt 2 ] && usage OUTDIR="${2%/}" @@ -60,7 +53,7 @@ done fini_gen() { gen $GEN_OUTS: - $GEN_FILES - phony ninja $NINJA_FILES + phony ninja: $NINJA_FILES wait } @@ -79,11 +72,11 @@ in_target_route() { [ ! "$TARGET" ] || has $DIR/$1 $TARGET_ROUTE } -shakeout() { +_shake_out() { _f=$3 if [ ! "$TARGET" ] || [ $TARGET = "$1" ]; then printf '' >$_f.tmp - $(type -P awk) -f $SHAKE_LIB/shakeout.awk >$_f.tmp + $(type -P cat) >$_f.tmp if cmp -s $_f.tmp $_f; then rm -f $_f.tmp else @@ -95,16 +88,16 @@ shakeout() { } sub() { - printf 'shake+subninja $dir/%s.ninja\n' "$1" + printf 'subninja $dir/%s.ninja\n' "$1" { [ "${2-}" ] && eval "$2 $1" $1 [ "${3-}" ] && eval "$3 $1" - } | shakeout "$DIR" "$OUTDIR" "$DIR/$1.ninja" + } | _shake_out "$DIR" "$OUTDIR" "$DIR/$1.ninja" } shake() { - printf 'shake+subninja $dir/%s/%s.ninja\n' "$1" "$NINJA_FN" + printf 'subninja $dir/%s/%s.ninja\n' "$1" "$NINJA_FN" NINJA_FILES="$NINJA_FILES $dir/$1/ninja" if in_target_route $1; then @@ -122,35 +115,89 @@ shake() { . $DIR/Shakefile [ "${2-}" ] && eval "$3 $1" fini_gen - } | shakeout "$DIR/$1" "$OUTDIR/$1" "$DIR/$1/$NINJA_FN.ninja" & + } | _shake_out "$DIR/$1" "$OUTDIR/$1" "$DIR/$1/$NINJA_FN.ninja" & fi } var() { - printf 'shake+var %s %s\n' "$1" "${*:2}" + printf '%s =' "$1" + for _v in ${*:2}; do + printf ' %s' "$_v" + done + printf '\n' eval "$1='\$$1'" } bind() { - printf 'shake+bind %s %s\n' "$1" "${*:2}" + printf ' %s =' "$1" + for _v in ${*:2}; do + printf ' %s' "$_v" + done + printf '\n' +} + +_shake_prefix() { + case "$2" in + '$'*|'./'*|'../'*|'/'*) + printf ' %s' "$2" + ;; + *) + printf ' %s/%s' "$1" "$2" + ;; + esac +} + +_shake_build() { + if [ "${2%%$1*}" ]; then + _shake_prefix $_pre "${2%%$1*}" + fi + + case "$1" in + -) printf ' |';; + --) printf ' ||';; + :) + printf ': %s' $_brule + _pre=$dir + ;; + esac + + if [ "${2##*$1}" ]; then + _shake_prefix $_pre "${2##*$1}" + fi } build() { - printf 'shake+build %s\n' "$*" + _brule=$1 + _pre=$outdir + + printf 'build' + for _v in ${*:2}; do + case "$_v" in + *:*) _shake_build : "$_v";; + *-*) _shake_build - "$_v";; + *--*) _shake_build -- "$_v";; + *) _shake_prefix $_pre "$_v";; + esac + done + printf '\n' } rule() { - printf 'shake+rule %s\n' "$1" + printf 'rule %s\n' "$1" bind command "${*:2}" eval "$1() { build $1 \$*; }" } default() { - printf 'shake+default %s\n' "$*" + printf 'default' + for _v in $*; do + _shake_prefix $dir "$_v" + done + printf '\n' } phony() { - printf 'shake+build phony %s: %s\n' "$1" "${*:2}" + build phony $* } error() { @@ -176,11 +223,6 @@ foreach() { GEN_FILES="$GEN_FILES $DIR/$1" } -if [ ! -d "$SHAKE_LIB" ]; then - printf 'shake: cannot find path: %s\n' "$SHAKE_LIB" >&2 - exit 1 -fi - if [ "$TARGET" ] && [ ! -f "$TARGET/Shakefile" ]; then printf "shake: target is missing Shakefile: %s\n" "$TARGET" >&2 exit 1 @@ -240,9 +282,9 @@ mkdir -p $BUILDDIR . $DIR/Shakefile - phony build.ninja ninja + phony build.ninja: ninja bind generator 1 -} | shakeout "$DIR" "$OUTDIR" "$DIR/$NINJA_FN.ninja" +} | _shake_out "$DIR" "$OUTDIR" "$DIR/$NINJA_FN.ninja" if [ ! -L build.ninja ]; then rm -f build.ninja diff --git a/shakeout.awk b/shakeout.awk @@ -1,124 +0,0 @@ -BEGIN { - state = "" -} - -function reset(newstate) { - state = newstate - printf "\n" -} - -function add_prefix(str) { - if (str ~ /^(\$|\.\/|\.\.\/|\/)/) return str - if (state == "VAR") return str - if (state == "BOUT") return "$outdir/" str - if (state == "BIN") return "$dir/" str - if (state == "BDEP") return "$dir/" str - if (state == "BORD") return "$dir/" str - - printf "shake: error add_prefix()\n" > "/dev/stderr" - exit 1 -} - -function print_tail(start, i) { - for(i=start; i<=NF; i++) { - printf " %s", add_prefix($i) - } -} - -function process(cmd, str) { - while (match(str, /(:|--|-|[ \t]+)/)) { - tok = substr(str, 1, RSTART - 1) - sep = substr(str, RSTART, RLENGTH) - - if (tok != "" && tok != cmd) { - if (state == "BRUL") { - brule = tok - state = "BOUT" - } else if(state == "BOUT") { - if (outputs[tok] == 1) { - brule = "" - bouts = "" - state = "" - return - } - bouts = bouts " " add_prefix(tok) - outputs[tok] = 1 - } else { - printf " %s", add_prefix(tok) - } - } - - if (sep ~ /:/) { - printf "build %s: %s", bouts, brule - state = "BIN" - brule = "" - bouts = "" - } - - else if (sep ~ /--/ && state != "BORD") { - printf " ||" - state = "BORD" - } - - else if (sep ~ /-/ && state != "BDEP") { - printf " |" - state = "BDEP" - } - - str = substr(str, RSTART + RLENGTH) - } - - if (str != "") printf " %s", add_prefix(str) -} - -/^shake\+build / { - reset("BRUL") - process("shake+build", $0) - next -} - -/^shake\+var / { - reset("VAR") - printf "%s =", $2 - print_tail(3) - next -} - -/^shake\+bind / { - reset("VAR") - printf " %s =", $2 - print_tail(3) - next -} - -/^shake\+default / { - reset("BIN") - printf "default" - print_tail(2) - next -} - -/^shake\+subninja / { - reset("BIN") - printf "subninja %s", add_prefix($2) - next -} - -/^shake\+rule / { - reset("") - printf "rule %s", $2 - next -} - -state == "VAR" { - print_tail(1) - next -} - -{ - process("", $0) -} - -END { - print "" -}