Ningen: a simple Ninja build generator
Ningen is a work-in-progress build generator for Ninja.
With Ningen, you define your Ninja build targets and rules using a simple
TypeScript API, and it generates a build.ninja
file for you. The build files
are just regular TypeScript files, run with Deno, so you're
free to extend them and make them as complex and powerful as you need to.
Installation
- Install Deno: https://deno.land
- Install Ninja: https://ninja-build.org
- There's no need to download or install Ningen yourself, Deno will do that for you.
Getting started
TODO: Update these instructions when the new API works.
(Also take a look at the examples folder.)
Define a
BUILD.ts
file at the root of your repo. This file will define the build rules for your entire project. Make it executable:chmod +x BUILD.ts
Add this shebang line so that executing
BUILD.ts
will run it with Deno:#!/usr/bin/env -S deno run --allow-read --allow-write --unstable
Import the
root
function from Ningen, and add the following skeleton:import { root } from "https://gitlab.com/silvo/ningen/-/raw/master/mod.ts"; root((ng) => { // Rules and targets go here. ng.generate(); });
The
root
function will pass aBuilder
instance (conventionally calledng
, short for "ningen") to its callback. That builder lets you define Ninja rules and targets. The Ningen API for those looks very similar to the Ninja syntax.Be sure to call the
generate
method at the very end to generate thebuild.ninja
file. By default it will define a generator rule calledningen
that will cause Ninja to regenerate thebuild.ninja
file if theBUILD.ts
file changes (this behaviour can be overridden).Define Ninja rules using the
rule
method. e.g. this rule invokes a shell script calledappend.sh
:const appendRule = ng.rule({ name: "append", command: "./append.sh $in $out", implicit: ["append.sh"], });
The
implicit
argument does not have an equivalent in Ninja. It lets you list files in the rule itself that will be added as implicit inputs to any targets that use the rule. e.g. in the example above, if theappend.sh
script is modified, everything that uses theappend
rule will be rebuilt.Define Ninja build targets using the
build
method:ng.build({ rule: appendRule, inputs: ["file.txt"], outputs: ["file.txt.out"], });
You can define helper functions too, which is helpful when you have lots of files:
function append(src) { ng.build({ rule: appendRule, inputs: [src], outputs: [src + ".out"], }); } append("file1.txt"); append("file2.txt"); append("file3.txt");
The
glob
method lets you easily operate over many files:function append(src) { ng.build({ rule: appendRule, inputs: [src], outputs: [src + ".out"], }); } ng.glob("*.txt").forEach((src) => append(src));
You can also use globs directly in calls to
build
:ng.build({ rule: appendRule, inputs: ng.glob("*.txt"), outputs: ["everything.out"], });
Design notes
Relative files, //-paths, absolute paths, etc.
The only way to get relative paths to work is to use meta.import.url
. But that
gives you a file://
url, e.g.
file:///home/silvo/code/ningen/example/BUILD.ts
.
Resolving everything to an absolute path would work. Maybe that's the best idea?
const appendRule = rule({
name: "append",
command: "./append.sh $in $out",
srcs: files("append.sh"),
});
How to get ./append.sh
to execute in the right directory? Maybe needs a
cd {dir} && ...
at the start of the command? Yuck.