shake

minimal build system that generates Ninja build files

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

README.md

(6649B)


      1 # Shake
      2 
      3 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.
      4 
      5 ## Motivation
      6 
      7 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.
      8 
      9 ## Requirements
     10 
     11 - [Ninja](https://ninja-build.org)
     12 - POSIX Shell
     13 
     14 ## Installation
     15 
     16 Run the installation script:
     17 
     18 ```sh
     19 ./install --prefix=/usr/local
     20 ```
     21 
     22 ### Installation Options
     23 
     24 | Option | Description | Default |
     25 |--------|-------------|---------|
     26 | `--prefix=DIR` | Installation prefix | `/usr/local` |
     27 | `--destdir=DIR` | Staging directory for packaging | (none) |
     28 | `--shakelib=DIR` | Path to shake library | `/share/shake` |
     29 
     30 ## Quick Start
     31 
     32 1. **Create a Shakefile** in your project root:
     33 
     34 ```sh
     35 let CC    ${CC:-cc}
     36 let CFLAGS ${CFLAGS:--Wall -g}
     37 
     38 rule cc '$CC $CFLAGS -c $in -o $out'
     39     bind description 'CC $in'
     40 
     41 rule link '$CC $in -o $out'
     42     bind description 'LINK $out'
     43 
     44 cc   main.o: main.c
     45 link  hello: main.o
     46 
     47 default hello
     48 ```
     49 
     50 Shake's goal is to remain minimal, yet provide helpful functions to get you productive
     51 quickly. The syntax tries to stay close to Ninja's syntax, so it would be very helpful to
     52 [understand and read how Ninja works](https://ninja-build.org/manual.html).
     53 
     54 2. **Generate Ninja files**:
     55 
     56 ```sh
     57 shake
     58 ```
     59 
     60 3. **Build your project**:
     61 
     62 ```sh
     63 ninja
     64 ```
     65 
     66 That's it! On subsequent builds, Ninja will only rebuild what's changed. Running shake again is
     67 only necessary if you add a new Shakefile. Modifications to existing Shakefiles are detected
     68 and rebuilt by ninja.
     69 
     70 ## Recursive Builds
     71 
     72 Shake supports recursive builds - each subdirectory can have its own Shakefile:
     73 
     74 ```sh
     75 # Root Shakefile (extends the example above)
     76 shake lib
     77 
     78 let LIBDIR $dir/lib
     79 let CFLAGS $CFLAGS -I$LIBDIR
     80 
     81 cc   main.o: main.c - $LIBDIR/lib.h
     82 link  hello: main.o $LIBDIR/lib.a
     83 ```
     84 
     85 ```sh
     86 # lib/Shakefile
     87 let AR ${AR:-ar}
     88 
     89 rule ar '$AR rcs $out $in'
     90     bind description 'AR $out'
     91 
     92 cc  lib.o: lib.c
     93 ar  lib.a: lib.o
     94 ```
     95 
     96 Notice that `lib/Shakefile` inherits variables and rules from its parent such as
     97 CFLAGS and cc. In the root Shakefile, a `-` is used to indicate that the following
     98 files are dependencies and not inputs. See Ninja's manual. The `-` is identical to
     99 Ninja's `|`.
    100 
    101 To run it:
    102 
    103 ```sh
    104 shake lib
    105 ninja
    106 ```
    107 
    108 See `example/nested/` for a complete working example.
    109 
    110 ## Usage
    111 
    112 ```
    113 shake [options] [directory]
    114   -C dir     change to directory before building
    115   -l dir     override shake lib path
    116   -o dir     set output directory
    117   -h         show help
    118 ```
    119 
    120 The optional `directory` argument specifies a subdirectory to generate:
    121 
    122 ```sh
    123 shake lib     # Generate the lib/ subproject
    124 shake .       # Generate the current directory (no subproject)
    125 ```
    126 
    127 Shake automatically finds the project root by looking for a `.shake/` directory, so
    128 you can run shake from any subdirectory.
    129 
    130 ## Syntax Reference
    131 
    132 ### Variables
    133 
    134 Define variables with `let`:
    135 
    136 ```sh
    137 let CC ${CC:-cc}
    138 let CFLAGS -Wall -g
    139 ```
    140 
    141 Variables can reference environment variables with `${VAR:-default}` syntax. The let
    142 command essentially writes a variable to the ninja file, and defines a shell variable
    143 so you can refer to it from any Shakefile.
    144 
    145 ### Rules
    146 
    147 Define build rules with `rule`:
    148 
    149 ```sh
    150 rule cc '$CC $CFLAGS -c $in -o $out'
    151     bind description 'CC $in'
    152 ```
    153 
    154 The rule body specifies the command to run. Available variables:
    155 - `$in` - input files
    156 - `$out` - output files
    157 
    158 Use `bind` to set rule attributes:
    159 - `bind description` - human-readable description shown during build
    160 - `bind depfile` - path to dependency file
    161 - `bind generator` - mark as a generator rule
    162 - `bind restat` - re-stat outputs after command runs
    163 
    164 ### Build Rules
    165 
    166 Declare what to build:
    167 
    168 ```sh
    169 cc    main.o: main.c
    170 cc   hello.o: hello.c
    171 link   hello: main.o hello.o
    172 ```
    173 
    174 Multiple inputs and outputs are supported.
    175 
    176 ### Phony Targets
    177 
    178 Create phony targets for actions that aren't actual files:
    179 
    180 ```sh
    181 phony clean rm -f *.o hello
    182 phony install $DESTDIR$PREFIX/bin/hello: hello
    183 ```
    184 
    185 ### Default Targets
    186 
    187 Set which targets to build when none is specified:
    188 
    189 ```sh
    190 default hello
    191 ```
    192 
    193 ### Subdirectory Builds
    194 
    195 Include subdirectory builds with `shake`:
    196 
    197 ```sh
    198 # In parent Shakefile
    199 shake lib
    200 shake bin
    201 ```
    202 
    203 Each subdirectory should have its own `Shakefile`. Shake generates Ninja files for each subproject and includes them.
    204 
    205 ### Directory Variables
    206 
    207 Two special variables are automatically available:
    208 - `$dir` - the directory containing the current Shakefile
    209 - `$outdir` - the output directory (can be overridden with `-o` or let command)
    210 
    211 ### Import Files
    212 
    213 Include shared definitions with `import`:
    214 
    215 ```sh
    216 import rules.sh
    217 ```
    218 
    219 ### Grouping build inputs and outputs
    220 
    221 Build inputs and outputs are tracked by shake. You can group inputs or outputs
    222 using an extended regular expression pattern to create a phony target.
    223 
    224 For example, the following creates a phony headers target and groups all generated header files:
    225 
    226 ```sh
    227 groupout headers '.*\.h$'
    228 ```
    229 
    230 If you need to fetch source files and extract them into `src/` subdirectory before
    231 compiling, the following groups all src files to depend on the phony fetch target:
    232 
    233 ```sh
    234 groupin fetch '\$dir/src/.*'
    235 ```
    236 
    237 ## Examples
    238 
    239 See the `example/` directory for working examples:
    240 
    241 ### Simple Example
    242 
    243 A minimal single-directory C project:
    244 
    245 ```sh
    246 cd example/simple
    247 shake
    248 ninja
    249 ```
    250 
    251 Builds `hello` from `main.c` and `hello.c`.
    252 
    253 ### Nested Example
    254 
    255 A multi-directory project with recursive subbuilds:
    256 
    257 ```sh
    258 cd example/nested
    259 shake
    260 ninja
    261 ```
    262 
    263 Structure:
    264 - `lib/` - builds a static library
    265 - `bin/` - builds an executable linking against the library
    266 
    267 ## How It Works
    268 
    269 Shake reads your `Shakefile` and generates `local.ninja` (and `build.ninja` symlink). When you run `ninja`, it executes the actual builds.
    270 
    271 Because Shake generates Ninja files, you get:
    272 - Fast incremental builds (Ninja tracks dependencies)
    273 - Parallel builds with `ninja -jN`
    274 - Build visualization with `ninja -t deps` and `ninja -t targets`