samurai: a ninja-compatible build tool written in C99
ninja-compatible build tool written in C
At a glance
- What is it?
- samurai is a build tool that speaks the ninja build language, written in C99 with a focus on simplicity, speed and portability, and it ships a single small binary called samu. The README is unusually precise about where it differs from ninja, listing four behavioural differences and the one feature it does not implement, which makes it one of the easier tools of its kind to evaluate before you switch.
- Who is it for?
- Adopt samurai if you already have a ninja build and want the same behaviour from a smaller, simpler, faster binary, and if your build does not depend on MSVC dependency handling. Do not adopt it if your build relies on the exact job ordering ninja happens to produce, because samurai schedules on a stack and the last scheduled job runs first, and do not adopt it if your build passes options after operands, since samurai does not permute arguments.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 17 days ago.
- What is it written in?
- Mainly C, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A small C program that reads ninja build files
samurai is a ninja-compatible build tool written in C99 with a focus on simplicity, speed, and portability. The whole thing is one binary, named samu, and the repository is a flat list of C files that match the stages of a build tool: parse.c and scan.c for reading the build file and the source tree, graph.c and deps.c for the dependency graph, build.c and tool.c for running commands, htab.c for the hash table, tree.c, env.c, log.c and util.c for the rest.
The name change from the project name to the binary is a small thing that tells you the design intent. A tool whose selling point is simplicity usually has one command and not a family of them.
The implementation is deliberately plain. The build file is the input, the same one ninja would use, and the output is the same set of artefacts. The licence in the repository is a custom file that GitHub cannot classify, which for a project of this size is worth reading before you vendor it into a distribution.
The maintenance record is a bit lumpy. The release history is 1.1 in March 2020, 1.2 in December 2020, and 1.3 on 2026-04-05, and the last push was on 2026-10-05, so the 1.3 line is where current work lands. The project is built on both sourcehut and GitHub Actions, with the .builds/ directory for the former and a workflow for the latter, and the .clang-format file is present, so the code is formatted to a defined style.
The audience is a C or systems person who has a ninja build and wants a tool with no runtime to install and no language to build it in.
The build language matches ninja through 1.9.0, with one exception
The status section of the README is the most useful paragraph in the project, and it is short.
samurai implements the ninja build language through version 1.9.0, except for MSVC dependency handling, that is, the deps = msvc feature. It uses the same format for .ninja_log and .ninja_deps as ninja, currently version 5 and 4 respectively. And it is feature-complete and supports most of the same options as ninja.
Two things follow from that. The exception is well defined: if your build file uses deps = msvc, samurai will not do what you expect, and that is a single line in the README rather than a discovery you make after an afternoon of debugging on a platform you cannot easily reproduce.
The log formats matter more than they look. The .ninja_log is the record of which commands ran and when, and the .ninja_deps is the record of the header dependencies discovered by the compiler. If a build system writes these, then switching to samurai and back is not a destructive operation, and the dependency information that ninja accumulated is not thrown away. The version numbers are given so you can tell whether the files on disk are in a format this tool understands.
So the migration story is: a ninja build file, the same artefacts, the same log files, and a different program reading them.
Building it with make, and the CFLAGS switches for odd platforms
The build is a plain make invocation, and the Makefile in the repository is a small, readable file that also documents the portability switches.
OS=posix
PREFIX?=/usr/local
BINDIR?=$(PREFIX)/bin
MANDIR?=$(PREFIX)/share/man
ALL_CFLAGS=$(CFLAGS) -std=c99 -Wall -Wextra -Wshadow -Wmissing-prototypes -Wpedantic -Wno-unused-parameter
LDLIBS?=-lrtThe samurai binary is built with make, and CFLAGS and LDLIBS can be set in the environment or straight on the command line. The compiler flags are strict by default, with warnings on shadowing, missing prototypes and pedantic issues, which is a reasonable standard for a tool that has to compile on whatever you have.
The requirements section explains the three switches you might need, and each corresponds to a real platform problem.
Scheduling jobs based on load average requires the non-standard getloadavg function, and that feature can be enabled by defining HAVE_GETLOADAVG in your CFLAGS, along with any other necessary definitions for your platform. So the load-aware scheduling is opt in, because the function it needs is not in POSIX.
Spawning subprocesses is done using posix_spawn, and if that interface is not available on your operating system, define NO_POSIX_SPAWN in your CFLAGS to use fork and exec instead.
And samurai uses clock_gettime, which requires -l rt when linking on some operating systems to ensure the interface is available. While it is a POSIX requirement to support this flag even if it is a no-op, some operating systems do not, and if you get an error about a missing rt library you can build without it by clearing LDLIBS.
That is three switches, each documented with the symptom it fixes, which is more than most build tools offer.
The four documented differences from ninja
The README has a section titled Differences from ninja, and its framing is careful: samurai tries to match ninja behavior as much as possible, but there are several cases where it is slightly different. Four are listed, and all of them are behavioural rather than cosmetic.
The first is variable lookup order. samurai uses the variable lookup order documented in the ninja manual, while ninja has a quirk, tracked as ninja-build/ninja#1516, where if the build edge has no variable bindings, the variable is looked up in file scope before the rule-level variables. So on a build file that relies on the quirk, the two tools can resolve a variable to different values.
The second is scheduling order, and it is the one with teeth. samurai schedules jobs using a stack, so the last scheduled job is the first to execute, while ninja schedules jobs based on the pointer value of the edge structure, since they are stored in a std::set of Edge pointers, so the first to execute depends on the address returned by malloc. The README is blunt about the consequence: this may result in build failures due to insufficiently specified dependencies in the project's build system.
The third is output handling. samurai does not post-process the job output in any way, so escape sequences are preserved, while ninja strips them if standard output is not a terminal. The practical note is that some build systems, like meson, force colour output from gcc by default with -fdiagnostics-color=always, so if you plan to save the output to a log you should pass -Db_colorout=auto to meson.
The fourth is argument parsing. samurai follows the POSIX Utility Syntax Guidelines, in particular guideline 9, so it requires that any command-line options precede the operands, and it does not do GNU-style argument permutation.
That last one is the easiest to trip over, and it is the kind of thing that turns into a confusing error rather than an obvious one.
The scheduling difference is the one to test first
Of the four differences, the scheduling order deserves a section of its own, because it is the one that can break a build that works today.
The mechanism is worth understanding rather than just noting. ninja keeps its edges in a std::set of Edge pointers, and it schedules jobs based on the pointer value of the edge structure, which means the order in which ready jobs run depends on the address malloc returned for them. That is not a designed ordering at all, it is an artefact of the container's memory layout, and it happens to be stable for a given run but is not a contract.
samurai uses a stack instead, so the last job scheduled is the first one executed. That is a defined, deterministic behaviour, and it is in a sense more predictable than what ninja does. But predictable is not the same as compatible.
The README draws the conclusion itself: this may result in build failures due to insufficiently specified dependencies in the project's build system. A build file that only works because of the order ninja happened to pick is a build file with a missing dependency edge, and samurai will expose it.
That makes the first step for anyone evaluating samurai obvious, and it is not to install it. It is to look at your build file and ask whether every output depends explicitly on every input it needs. If the answer is yes, the scheduling difference is invisible. If the answer is uncertain, samurai is the tool that will tell you, because it will reorder the jobs and the failure will appear.
The same reasoning applies to the variable lookup difference. A build file that relies on the ninja quirk about file scope being consulted before rule-level variables is relying on undocumented behaviour, and the documented order is the one a new build file should be written against anyway.
Editorial conclusion
Adopt samurai if you already have a ninja build and want the same behaviour from a smaller, simpler, faster binary, and if your build does not depend on MSVC dependency handling. Do not adopt it if your build relies on the exact job ordering ninja happens to produce, because samurai schedules on a stack and the last scheduled job runs first, and do not adopt it if your build passes options after operands, since samurai does not permute arguments. Verify first that your build file stays inside the implemented language, that the .ninja_log and .ninja_deps versions in use are the ones it writes, and that the documentation for the four documented differences has been read against your own build system.
Frequently asked questions
What is samurai and how does it relate to ninja?
samurai is a ninja-compatible build tool written in C99 with a focus on simplicity, speed and portability. It implements the ninja build language through version 1.9.0, except for MSVC dependency handling, and it writes the same .ninja_log and .ninja_deps formats as ninja, currently version 5 and 4.
How do I build samurai?
Run make, since samurai can be built with make and CFLAGS and LDLIBS can be set in the environment or on the command line. The default compiler flags are strict, with -std=c99, -Wall, -Wextra, -Wshadow, -Wmissing-prototypes and -Wpedantic, and LDLIBS defaults to -lrt.
What does samurai not support that ninja does?
MSVC dependency handling, that is the deps = msvc feature, is the one gap in the implemented build language. The README also lists four behavioural differences, including variable lookup order, job scheduling order, escape sequence handling in output, and a requirement that command-line options precede operands.
Why might a build that works with ninja fail with samurai?
samurai schedules jobs using a stack, so the last scheduled job executes first, while ninja schedules based on the pointer value of the edge structure. The README notes that this may result in build failures due to insufficiently specified dependencies in the project's build system.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/michaelforney-samurai)Was this useful?