shake

minimal build system that generates Ninja build files

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

commit 278471491bbf7a09ebaa1569418c037f247b25c6
parent b5e2d04b752c18164bfa4c1a993e952f33be6bde
Author: Jul <jul@9o.is>
Date:   Sat, 14 Mar 2026 17:00:20 +0800

add readme

Diffstat:
AREADME.md | 256+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mshake | 2+-
2 files changed, 257 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md @@ -0,0 +1,256 @@ +# Shake + +Shake is a minimal build system that generates [Ninja](https://ninja-build.org) build files from declarative `Shakefile` definitions, which are just POSIX shell scripts. It provides a clean, composable way to describe recursive builds without the complexity of traditional build systems like Make. + +## Motivation + +Shake, a portmanteau of shell and make, is a generalized set of posix shell functions I wrote to build my statically-linked linux distro from source, but it's flexible enough to be used in a wide-variety of tasks, like managing dot files or even server deployment scripts. Since it's powered by Ninja, you get the benefit of incremental, recursive builds. Why shell? Because it's a commonly used language for orchestrating tasks on unix systems. Still, the orchestration gets chaotic and become difficult to manage very quickly at scale. Incorporating a build backend with dependency management is a solution to that problem. + +## Requirements + +- [Ninja](https://ninja-build.org) +- POSIX Shell + +## Installation + +Run the installation script: + +```sh +./install --prefix=/usr/local +``` + +### Installation Options + +| Option | Description | Default | +|--------|-------------|---------| +| `--prefix=DIR` | Installation prefix | `/usr/local` | +| `--destdir=DIR` | Staging directory for packaging | (none) | +| `--shakelib=DIR` | Path to shake library | `/share/shake` | + +## Quick Start + +1. **Create a Shakefile** in your project root: + +```sh +var CC ${CC:-cc} +var CFLAGS ${CFLAGS:--Wall -g} + +rule cc '$CC $CFLAGS -c $in -o $out' + bind description 'CC $in' + +rule link '$CC $in -o $out' + bind description 'LINK $out' + +cc main.o: main.c +link hello: main.o + +default hello +``` + +Shake's goal is to remain minimal, yet provide helpful functions to get you productive +quickly. The syntax tries to stay close to Ninja's syntax, so it would be very helpful to +[understand and read how Ninja works](https://ninja-build.org/manual.html). + +2. **Generate Ninja files**: + +```sh +shake +``` + +3. **Build your project**: + +```sh +ninja +``` + +That's it! On subsequent builds, Ninja will only rebuild what's changed. Running shake again is +only necessary if you add a new Shakefile. Modifications to existing Shakefiles are detected +and rebuilt by ninja. + +## Recursive Builds + +Shake supports recursive builds - each subdirectory can have its own Shakefile: + +```sh +# Root Shakefile (extends the example above) +shake lib + +var LIBDIR $dir/lib +var CFLAGS $CFLAGS -I$LIBDIR + +cc main.o: main.c - $LIBDIR/lib.h +link hello: main.o $LIBDIR/lib.a +``` + +```sh +# lib/Shakefile +var AR ${AR:-ar} + +rule ar '$AR rcs $out $in' + bind description 'AR $out' + +cc lib.o: lib.c +ar lib.a: lib.o +``` + +Notice that `lib/Shakefile` inherits variables and rules from its parent such as +CFLAGS and cc. In the root Shakefile, a `-` is used to indicate that the following +files are dependencies and not inputs. See Ninja's manual. The `-` is identical to +Ninja's `|`. + +To run it: + +```sh +shake lib +ninja +``` + +See `example/nested/` for a complete working example. + +## Usage + +``` +shake [options] [directory] + -C dir change to directory before building + -l dir override shake lib path + -o dir set output directory + -h show help +``` + +The optional `directory` argument specifies a subdirectory to generate: + +```sh +shake lib # Generate the lib/ subproject +shake . # Generate the current directory (no subproject) +``` + +Shake automatically finds the project root by looking for a `.shake/` directory, so +you can run shake from any subdirectory. + +## Syntax Reference + +### Variables + +Define variables with `var`: + +```sh +var CC ${CC:-cc} +var CFLAGS -Wall -g +``` + +Variables can reference environment variables with `${VAR:-default}` syntax. The var +command essentially writes a variable to the ninja file, and defines a shell variable +so you can refer to it from any Shakefile. + +### Rules + +Define build rules with `rule`: + +```sh +rule cc '$CC $CFLAGS -c $in -o $out' + bind description 'CC $in' +``` + +The rule body specifies the command to run. Available variables: +- `$in` - input files +- `$out` - output files + +Use `bind` to set rule attributes: +- `bind description` - human-readable description shown during build +- `bind depfile` - path to dependency file +- `bind generator` - mark as a generator rule +- `bind restat` - re-stat outputs after command runs + +### Build Rules + +Declare what to build: + +```sh +cc main.o: main.c +cc hello.o: hello.c +link hello: main.o hello.o +``` + +Multiple inputs and outputs are supported. + +### Phony Targets + +Create phony targets for actions that aren't actual files: + +```sh +phony clean rm -f *.o hello +phony install $DESTDIR$PREFIX/bin/hello: hello +``` + +### Default Targets + +Set which targets to build when none is specified: + +```sh +default hello +``` + +### Subdirectory Builds + +Include subdirectory builds with `shake`: + +```sh +# In parent Shakefile +shake lib +shake bin +``` + +Each subdirectory should have its own `Shakefile`. Shake generates Ninja files for each subproject and includes them. + +### Directory Variables + +Two special variables are automatically available: +- `$dir` - the directory containing the current Shakefile +- `$outdir` - the output directory (can be overridden with `-o` or var command) + +### Import Files + +Include shared definitions with `import`: + +```sh +import rules.sh +``` + +## Examples + +See the `example/` directory for working examples: + +### Simple Example + +A minimal single-directory C project: + +```sh +cd example/simple +shake +ninja +``` + +Builds `hello` from `main.c` and `hello.c`. + +### Nested Example + +A multi-directory project with recursive subbuilds: + +```sh +cd example/nested +shake +ninja +``` + +Structure: +- `lib/` - builds a static library +- `bin/` - builds an executable linking against the library + +## How It Works + +Shake reads your `Shakefile` and generates `local.ninja` (and `build.ninja` symlink). When you run `ninja`, it executes the actual builds. + +Because Shake generates Ninja files, you get: +- Fast incremental builds (Ninja tracks dependencies) +- Parallel builds with `ninja -jN` +- Build visualization with `ninja -t deps` and `ninja -t targets` diff --git a/shake b/shake @@ -14,7 +14,7 @@ GEN_FILES="./Shakefile $SHAKE_BIN" GEN_OUTS=$DIR/$NINJA_FN.ninja usage() { - printf "usage: shake [options] [target] + 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