A simple, fast, native C++23 build system with GN-style generation and Ninja-style execution.
C++
1
38 commits
updated Oct 9, 2026
A simple, fast, native C++23 build system with GN-style generation and Ninja-style execution.
NightBuild is a native macOS build system designed to make project manifests easy to write while keeping build execution fast.
The goal is simple:
Easy project description → concrete build graph → fast execution.
No CMake language to learn. No enormous build-system configuration. No generated build.ninja sitting between your project and the executor.
Clone NightBuild and run the bootstrap:
git clone https://github.com/Lord-Grumpypants/nightbuild.git
cd nightbuild
python3 bootstrap.py
C++ projects shouldn't need a complicated build system just to compile a few source files.
A small NightBuild project can look like this:
project("Hello")
executable("hello"):
sources = ["main.cpp"]
cxxflags = ["-O3", "-flto=full"]
ldflags = ["-flto=full"]
Then:
nightbuild gen -C out/Official
nightbuild build -C out/Official
That's it.
NightBuild turns the manifest into a concrete build graph during gen, then executes that graph directly during build.
BUILD.nb manifestsdeclare_args and args.nbpkg-config integrationNightBuild supports configurable build arguments through declare_args and args.nb.
Arguments are declared in BUILD.nb:
declare_args("lto"):
default = true
if lto:
ldflags += ["-flto=full"]
A project can declare as many arguments as it needs. Arguments have defaults, so a project remains usable even when no args.nb file exists.
Configuration overrides live in the output directory:
out/Official/args.nb
For example:
lto = false
Apply the configuration with:
nightbuild args -C out/Official
Then build normally:
nightbuild build -C out/Official
args.nb only overrides arguments explicitly declared by the project. Unknown arguments are rejected.
This keeps project configuration separate from the project manifest while allowing the same BUILD.nb to produce different build configurations.
The intended flow is:
BUILD.nb
↓
declare_args + defaults
↓
args.nb overrides
↓
evaluate BUILD.nb
↓
concrete build graph
↓
build
Arguments configure the project; they do not modify targets after generation.
NightBuild supports normal target dependencies:
project("Hello")
executable("hello"):
sources = ["main.cpp"]
deps = ["hellolib"]
cxxflags = ["-O3", "-flto=full"]
ldflags = ["-flto=full"]
static_library("hellolib"):
sources = ["hello-lib.cpp"]
cxxflags = ["-O3", "-flto=full"]
NightBuild understands that hello depends on hellolib, builds the library first, and then links the executable.
The resulting static library follows the conventional naming scheme:
libhellolib.a
Generate a build directory:
nightbuild gen -C out/Official
Configure a build directory using args.nb:
nightbuild args -C out/Official
Build everything:
nightbuild build -C out/Official
Build a specific target:
nightbuild build -C out/Official hello
Build multiple targets:
nightbuild build -C out/Official nightbuild test
Rebuild:
nightbuild rebuild -C out/Official
Clean generated objects:
nightbuild clean -C out/Official
Remove the generated build state completely:
nightbuild clobber -C out/Official
Verbose output:
nightbuild build -C out/Official -v
NightBuild deliberately separates generation from execution.
nightbuild gen reads the project's BUILD.nb, resolves targets and dependencies, loads toolchains, evaluates configuration, and produces a concrete command database.
The expensive decisions are made up front.
nightbuild args applies the project's args.nb configuration and regenerates the build graph.
This means configuration is explicit rather than being rediscovered during every build.
nightbuild build operates on that concrete graph.
There is no need to repeatedly parse a high-level build language or rediscover build rules while compiling.
This gives NightBuild a design that is conceptually similar to GN's generation model while retaining the direct execution model that makes Ninja fast.
NightBuild includes a dependency-aware parallel scheduler designed to prioritize work intelligently without turning scheduling into an expensive optimization problem.
Scheduling considers factors such as:
The scheduler is intended to make good decisions quickly rather than spend significant time trying to find a mathematically perfect schedule.
NightBuild tracks build state using hashes rather than relying solely on file modification times.
That means a build can determine whether work actually needs to be repeated instead of assuming that a timestamp change necessarily means the contents changed.
Unchanged work is skipped.
NightBuild is benchmarked against both Ninja and samu using the NightBuild project itself as the workload.
The current benchmark uses:
Current results:
| Build system | Average | Median |
|---|---|---|
| NightBuild | 6.643 s | 6.260 s |
| Ninja | 7.483 s | 7.090 s |
| samu | 9.502 s | 9.200 s |
NightBuild was the fastest in the 15-round tournament.
That's a benchmark of one project on one machine, not a claim that NightBuild will beat every build system on every workload. The purpose is to measure real progress and keep performance regressions visible.
Those were the results from our benchmark script, tournament.sh.
NightBuild uses a small Makefile to bootstrap itself.
The intended lifecycle is:
Makefile
↓
bootstrap NightBuild
↓
nightbuild gen
↓
nightbuild build
↓
optimized NightBuild
Make is used only to get the first NightBuild executable onto the machine. The canonical project description is BUILD.nb.
The full bootstrap process can additionally build an instrumented NightBuild, train its profile by compiling Ninja, merge the resulting LLVM profile data, and produce the optimized NightBuild executable.
The simplest development workflow is:
git clone https://github.com/Lord-Grumpypants/nightbuild.git
cd nightbuild
make
./nightbuild gen -C out/Official
./nightbuild build -C out/Official
After bootstrapping, NightBuild can build itself using its own build system.
For the full optimized bootstrap:
python3 bootstrap.py
The bootstrap script installs any required Homebrew dependencies, builds the bootstrap NightBuild, trains PGO using Ninja, and produces the final optimized binary.
NightBuild is intentionally not trying to become CMake.
The project favors:
Advanced features should exist when they solve real problems, but they should not make a simple project feel complicated.
The ideal experience is that someone learning C++ can describe their project without first having to learn an entire build-system language.
NightBuild is an active project and is still evolving.
The current focus is making the core build experience extremely solid before adding layers of complexity.
The project is particularly interested in improving:
NightBuild is licensed under the GNU General Public License v3.0 or later.
See LICENSE for the full license text.
A simple, fast, native C++23 build system with GN-style generation and Ninja-style execution.
C++
1
38 commits
updated Oct 9, 2026
A simple, fast, native C++23 build system with GN-style generation and Ninja-style execution.
NightBuild is a native macOS build system designed to make project manifests easy to write while keeping build execution fast.
The goal is simple:
Easy project description → concrete build graph → fast execution.
No CMake language to learn. No enormous build-system configuration. No generated build.ninja sitting between your project and the executor.
Clone NightBuild and run the bootstrap:
git clone https://github.com/Lord-Grumpypants/nightbuild.git
cd nightbuild
python3 bootstrap.py
C++ projects shouldn't need a complicated build system just to compile a few source files.
A small NightBuild project can look like this:
project("Hello")
executable("hello"):
sources = ["main.cpp"]
cxxflags = ["-O3", "-flto=full"]
ldflags = ["-flto=full"]
Then:
nightbuild gen -C out/Official
nightbuild build -C out/Official
That's it.
NightBuild turns the manifest into a concrete build graph during gen, then executes that graph directly during build.
BUILD.nb manifestsdeclare_args and args.nbpkg-config integrationNightBuild supports configurable build arguments through declare_args and args.nb.
Arguments are declared in BUILD.nb:
declare_args("lto"):
default = true
if lto:
ldflags += ["-flto=full"]
A project can declare as many arguments as it needs. Arguments have defaults, so a project remains usable even when no args.nb file exists.
Configuration overrides live in the output directory:
out/Official/args.nb
For example:
lto = false
Apply the configuration with:
nightbuild args -C out/Official
Then build normally:
nightbuild build -C out/Official
args.nb only overrides arguments explicitly declared by the project. Unknown arguments are rejected.
This keeps project configuration separate from the project manifest while allowing the same BUILD.nb to produce different build configurations.
The intended flow is:
BUILD.nb
↓
declare_args + defaults
↓
args.nb overrides
↓
evaluate BUILD.nb
↓
concrete build graph
↓
build
Arguments configure the project; they do not modify targets after generation.
NightBuild supports normal target dependencies:
project("Hello")
executable("hello"):
sources = ["main.cpp"]
deps = ["hellolib"]
cxxflags = ["-O3", "-flto=full"]
ldflags = ["-flto=full"]
static_library("hellolib"):
sources = ["hello-lib.cpp"]
cxxflags = ["-O3", "-flto=full"]
NightBuild understands that hello depends on hellolib, builds the library first, and then links the executable.
The resulting static library follows the conventional naming scheme:
libhellolib.a
Generate a build directory:
nightbuild gen -C out/Official
Configure a build directory using args.nb:
nightbuild args -C out/Official
Build everything:
nightbuild build -C out/Official
Build a specific target:
nightbuild build -C out/Official hello
Build multiple targets:
nightbuild build -C out/Official nightbuild test
Rebuild:
nightbuild rebuild -C out/Official
Clean generated objects:
nightbuild clean -C out/Official
Remove the generated build state completely:
nightbuild clobber -C out/Official
Verbose output:
nightbuild build -C out/Official -v
NightBuild deliberately separates generation from execution.
nightbuild gen reads the project's BUILD.nb, resolves targets and dependencies, loads toolchains, evaluates configuration, and produces a concrete command database.
The expensive decisions are made up front.
nightbuild args applies the project's args.nb configuration and regenerates the build graph.
This means configuration is explicit rather than being rediscovered during every build.
nightbuild build operates on that concrete graph.
There is no need to repeatedly parse a high-level build language or rediscover build rules while compiling.
This gives NightBuild a design that is conceptually similar to GN's generation model while retaining the direct execution model that makes Ninja fast.
NightBuild includes a dependency-aware parallel scheduler designed to prioritize work intelligently without turning scheduling into an expensive optimization problem.
Scheduling considers factors such as:
The scheduler is intended to make good decisions quickly rather than spend significant time trying to find a mathematically perfect schedule.
NightBuild tracks build state using hashes rather than relying solely on file modification times.
That means a build can determine whether work actually needs to be repeated instead of assuming that a timestamp change necessarily means the contents changed.
Unchanged work is skipped.
NightBuild is benchmarked against both Ninja and samu using the NightBuild project itself as the workload.
The current benchmark uses:
Current results:
| Build system | Average | Median |
|---|---|---|
| NightBuild | 6.643 s | 6.260 s |
| Ninja | 7.483 s | 7.090 s |
| samu | 9.502 s | 9.200 s |
NightBuild was the fastest in the 15-round tournament.
That's a benchmark of one project on one machine, not a claim that NightBuild will beat every build system on every workload. The purpose is to measure real progress and keep performance regressions visible.
Those were the results from our benchmark script, tournament.sh.
NightBuild uses a small Makefile to bootstrap itself.
The intended lifecycle is:
Makefile
↓
bootstrap NightBuild
↓
nightbuild gen
↓
nightbuild build
↓
optimized NightBuild
Make is used only to get the first NightBuild executable onto the machine. The canonical project description is BUILD.nb.
The full bootstrap process can additionally build an instrumented NightBuild, train its profile by compiling Ninja, merge the resulting LLVM profile data, and produce the optimized NightBuild executable.
The simplest development workflow is:
git clone https://github.com/Lord-Grumpypants/nightbuild.git
cd nightbuild
make
./nightbuild gen -C out/Official
./nightbuild build -C out/Official
After bootstrapping, NightBuild can build itself using its own build system.
For the full optimized bootstrap:
python3 bootstrap.py
The bootstrap script installs any required Homebrew dependencies, builds the bootstrap NightBuild, trains PGO using Ninja, and produces the final optimized binary.
NightBuild is intentionally not trying to become CMake.
The project favors:
Advanced features should exist when they solve real problems, but they should not make a simple project feel complicated.
The ideal experience is that someone learning C++ can describe their project without first having to learn an entire build-system language.
NightBuild is an active project and is still evolving.
The current focus is making the core build experience extremely solid before adding layers of complexity.
The project is particularly interested in improving:
NightBuild is licensed under the GNU General Public License v3.0 or later.
See LICENSE for the full license text.