Self-hosted service
google/kati avatar
google/kati

kati does not build faster on its own, it hands your Makefile to ninja

An experimental GNU make clone

1,027 stars135 forksRustApache-2.0

At a glance

What is it?
kati is Google's experimental GNU make clone whose stated purpose is to speed up Android's incremental build, and the honest detail in the README is that it does not deliver the speedup itself, it converts your Makefile into a ninja file and lets ninja do the work. The C++ implementation ckati is what Android uses, and a Rust port rkati sits beside it without plans to replace it.
Who is it for?
kati fits an Android platform engineer whose build is still make-based but wants ninja's scheduling, and who is willing to run an experimental tool in a controlled place. It does not fit a team whose build is not make-based, and it does not fit anyone who needs a general-purpose faster make, because the project states plainly that kati on its own does not provide a faster build.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 9 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • google/kati1,027 ★ · +2
Star history
Repository2026-10-192026-11-07
google/kati1k1k

2 Stars in the last 19 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The speedup comes from ninja, not from kati

The README is unusually candid, and the candour is the most useful thing in it. The main goal of this tool is to speed-up incremental build of Android. Currently, kati does not offer a faster build by itself. It instead converts your Makefile to a ninja file.

That is a two-stage design, and understanding it is the difference between evaluating kati correctly and evaluating it wrongly. You do not swap make for kati and get a faster build. You run kati over your makefiles, it produces a ninja file describing the same graph of build steps, and ninja executes that graph. The acceleration comes from the second stage, from ninja's dependency scheduling, and kati is the translator that lets a make-based build reach that stage.

This is the reason the project exists at all. Android's build is a large make-based system, and make's recursive evaluation and its serial-by-default behaviour are a poor fit for the parallelism modern hardware offers. Rewriting an entire platform build in ninja syntax is not realistic, so the pragmatic answer is to keep the makefiles and pay for a translation step.

The consequence for an adopter is that the correct comparison is not make versus kati but make versus kati plus ninja. If your build is not make-based, or if you have already moved to ninja, kati has nothing to do for you.

The project describes itself simply, as an experimental GNU make clone, and that word experimental is doing real work in that sentence. It is a reimplementation, not a wrapper, and its fidelity to GNU make semantics is the thing that has to be earned and tested.

Android N and later already run ckati and ninja for you

The section on Android use is short because the answer for most people is that it is already happening.

For Android-N+, ckati and ninja is used automatically, and there is a prebuilt checked in under prebuilts/build-tools that is used. All of Android's build commands, m, mmm, mmma and the rest, should just work.

That is the adoption story for the tool in practice. A platform developer building Android does not install kati, they invoke m or mmm as usual, and the build system underneath has been running ckati and ninja for some time. The tool is infrastructure rather than a product, and its users are the people writing the makefiles and the ones debugging incremental build behaviour.

The prebuilt is worth noticing as a distribution decision. A binary that has to be checked into the build tree is a binary that has to be rebuilt and kept in sync with the source, and it is a binary that every engineer gets without installing anything. The alternative, requiring every developer to have a matching ckati on their path, would put a version constraint in front of every checkout.

The requirement that the build commands just work is also the real acceptance criterion for a make clone. A make implementation that differs in any observable way from GNU make produces build systems that behave differently on different machines, and that class of bug is the hardest kind to diagnose in a platform build.

Two implementations, one Go test harness

The repository holds two implementations and one test harness, and the structure is easy to read from the top-level entries.

The C++ implementation lives in src/ and is built as ckati, with a Makefile.ckati and a Makefile.ckati-related clean target. The Rust port lives in src-rs/ and builds as rkati. The test harness is Go, with run_test.go at the root and a go.mod that declares the module as github.com/google/kati and pins glog and go-diff.

That Go harness is the interesting part, because it is what makes the project trustworthy. The tests are driven by flags that select which implementation to exercise and which build engine to compare against.

sh
$ make test
$ go test --ckati
$ go test --ckati --ninja
$ go test --ckati --ninja --all

The first command runs the make target, which builds both the binary and the test dependencies. The go test invocations run all cKati and Ninja tests in the testcases/ directory, and the --all flag widens the scope. The pattern is differential: run the same testcase through GNU make, through ckati, and through ckati generating a ninja file, and check that the results agree. That is a far stronger guarantee than a set of golden-output unit tests, because it tests equivalence with the thing being cloned rather than with a recorded expectation.

The rust version of the same harness exists too, with the rkati flags, and the Makefile has a matching rkati_test target that runs cargo test alongside the go test invocations. The repository also has a benches/ directory and a criterion-based benchmark, which in the Rust port is a named strutil_benchmark with the harness disabled.

The test environment pins make 4.2.1 and ninja 1.11.1

Compatibility with a specific GNU make is the central constraint on a make clone, and the repository handles it by pinning the environment rather than by hoping.

The Dockerfile starts from ubuntu:22.04 and installs make, git-core, build-essential, curl, python3, wget and unzip. It then downloads Go 1.14.9, and then does the interesting thing. It downloads the make-dfsg package version 4.2.1-1.2, unpacks the deb, and moves the make binary into its own directory on the path, with the comment that it emulates Make 4.2.1 instead of the default 4.3 currently provided.

So the test environment deliberately does not use the make that ships with the distribution. It uses the make that kati is currently compatible with, and the README gives the same instructions to a developer working on a machine with a different make version, unpacking the same deb into a temporary directory and putting it first on the path.

Ninja is pinned too, for the opposite reason. The Dockerfile downloads ninja v1.11.1 from the upstream releases, with the comment that a newer version than the one in apt is needed.

The Makefile has an info target that prints the GNU make version, the ckati version, the bash version and the shell version, which is exactly the information you need when a test fails and you need to know which environment produced the difference.

The two of these together define the project's real contract. kati is not claiming to work with every GNU make, it is claiming to work with the versions Android uses, and the test environment is arranged to prove that claim on every commit.

rkati is a drop-in replacement, not a planned successor

The Rust port has an unusually clear status, and the README does not hedge about it. There is an experimental rust port in the src-rs/... directory, though there are no plans for it to replace the C++ version. It should have a very similar feature set to the C++ version, and similar performance, in release mode.

The build is a plain cargo invocation, and the test harness mirrors the C++ one.

sh
$ cargo build
$ go test --rkati
$ go test --rkati --ninja
$ go test --rkati --ninja --all

The resulting rkati binary in target/*/rkati is a drop-in replacement for ckati, and setting KATI_LOG=trace at runtime can enable extra debug logging.

That last sentence is the kind of detail that saves an afternoon. The Cargo.toml shows a default-run of rkati, so a bare cargo build produces the Rust binary, and it shows two binaries, rkati from src-rs/main.rs and find_emulator from src-rs/bins/find_emulator.rs, with a library target at src-rs/lib.rs. The dependency list is small and modern, with anyhow, bytes, criterion for the benchmarks, env_logger, libc, log, memchr, os_pipe and parking_lot, plus a Linux-only tikv-jemallocator and an optional gperftools feature behind a gperf flag. The edition is 2024 and the package version is 0.1.0.

The name find_emulator deserves a note, because it hints at the awkward part of the problem. A make clone needs to reproduce the behaviour of shell functions and command substitution, and on Android that means finding an appropriate emulator for binaries built for a different architecture. A helper binary whose job is to locate one is a small but telling artefact of what it takes to be make-compatible on a platform with cross-compilation targets.

The honest assessment is that rkati is an experiment in the experimental tool, and a reader should treat its similar performance claim as a goal the project is testing rather than a settled property.

Apache-2.0, no releases, and a repository that mixes C++, Rust and Go

The licence is Apache-2.0, with the LICENSE file at the repository root and the standard Apache header at the top of the Makefile. You can use, modify and redistribute it, including in commercial products, provided the notice and licence conditions are met. That is a licensing fact rather than legal advice, and for a Google project under a permissive licence it is about as permissive as it gets.

The repository is not archived, the default branch is master, and the last push was on 2026-09-19. There are no GitHub releases, which is consistent with the tool being consumed as checked-in prebuilts inside the Android tree rather than as a download.

The repository also carries a mix of build systems that describes the project accurately. The C++ side is built with a Makefile, since the project is a make clone and using make to build it is fitting. The Rust side uses Cargo, with a Cargo.lock at the root. The test harness uses Go, and the Go module is pinned to go 1.14, matching the Go 1.14.9 toolchain the Dockerfile installs. The Go version being old is not an oversight, it is the toolchain the harness was written against.

Alongside those there is an Android.bp, which is the Android build system's own description format, so that kati can be built as part of the platform tree, a CODEOWNERS file, an INTERNALS.md document, a clang-format-check script, a .clang-format file, an AUTHORS file, a CONTRIBUTORS file and a CONTRIBUTING.md. The .github directory holds the Build and Test workflow.

The overall picture is a project with one production consumer, a clear test methodology, two implementations, and an explicit statement that it is experimental. For a reader deciding whether to depend on it, that combination is a good sign of honesty and a caution about commitment.

Editorial conclusion

kati fits an Android platform engineer whose build is still make-based but wants ninja's scheduling, and who is willing to run an experimental tool in a controlled place. It does not fit a team whose build is not make-based, and it does not fit anyone who needs a general-purpose faster make, because the project states plainly that kati on its own does not provide a faster build. Before adopting it, read INTERNALS.md, build ckati with make ckati, and compare the generated ninja file against your current build, then run the Go test suite with the --ckati and --ninja flags to see whether the conversion is faithful for your makefiles.

Frequently asked questions

Does kati make the build faster on its own?

No. The README states that currently kati does not offer a faster build by itself, and that it instead converts your Makefile to a ninja file. The stated main goal is to speed up the incremental build of Android, and that speedup comes from ninja executing the generated file.

How do I build kati and run its tests?

Run make ckati, which produces a ckati binary in the project root, then make test. The Go harness is driven directly with go test --ckati, go test --ckati --ninja and go test --ckati --ninja --all, which run the cKati and Ninja tests in the testcases/ directory.

What is the difference between ckati and rkati?

ckati is the C++ implementation in src/, and rkati is an experimental Rust port in src-rs/. The README says there are no plans for the Rust version to replace the C++ one, and that the resulting rkati binary in target/*/rkati is a drop-in replacement for ckati.

Is kati already used in the Android build?

For Android-N and later, ckati and ninja are used automatically, with a prebuilt checked in under prebuilts/build-tools. The README says all of Android's build commands, including m, mmm and mmma, should just work.

How do I enable extra debugging in kati?

Setting KATI_LOG=trace at runtime enables extra debug logging, according to the README. The Makefile also has an info target that prints the GNU make version, the ckati version, the bash version and the shell version, which is useful when a test fails.

Official sources

  1. google/kati on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/google-kati.svg)](https://hysenlabs.com/projects/google-kati)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/google-kati.svg)](https://hysenlabs.com/projects/google-kati)

Was this useful?