After nine years, Ninja has merged support for the GNU Make jobserver
Over nine years ago, on April 27, 2016, an issue was opened on the Ninja GitHub issue tracker requesting support for the GNU Make jobserver. Now, after multiple failed pull requests and a few soft forks, it has finally been added and released.
And I promise you: this is awesome!
What does this solve?
Ninja is a build system like GNU Make: you give it the list of files you want created (the outputs), how it should create them (the rules), and what each one’s dependencies are (the inputs). But you need to ensure it creates files in parallel when possible. It must not re-create files that already exist, but must re-create files when inputs change. Many other tiny annoying details need to be handled as well.
Let’s focus on one specific detail: parallelism. Almost all build systems run in parallel to maximize resource utilization and minimize execution time. Ninja specifically defaults to running one process per CPU thread. This works great most of the time!
Except… what if one of those processes is another instance of Ninja? Now in the worst-case scenario (assuming an 8-core/16-thread CPU), you have the parent instance of Ninja managing 16 processes and another child instance managing 16 more threads! That adds up to 32 processes total on an eight-core CPU. You can see how this can quickly get out of hand and lead to massive resource over-utilization and even system freezes.
There are ways to work around this. For instance, you can disable parallelism on the child Ninja instance(s), but while that will fix over-utilization, it will lead to resource under-utilization and slower execution times. You can also try manually tweaking parallelism levels. However, that will cause inconsistent and inefficient behavior when using multiple build machines.
And this is a real problem affecting real projects. Features like CMake’s ExternalProject
often lead to recursive Ninja calls. These recursive calls can easily cause the parallelism problem described above. For reference, see these discussion posts.
But what if I told you this problem has already been solved… back in 1999?
Introducing: The GNU Make Jobserver
GNU Make 3.78 introduced a new feature: the jobserver.
This was created because GNU Make experienced the same problem as Ninja: recursive GNU Make calls could easily cause resource over-utilization and system freezes. Previously, this had been “solved” by disabling parallelism in child GNU Make calls, but as I mentioned earlier, this led to resource under-utilization and longer build times.
The jobserver was a proper solution. The parent GNU Make instance would act as the server. All child instances would be clients. Anytime a client wanted to launch a process, it would ask the server to tell it when it was allowed to. This allowed the server to ensure resources were not over- or under-utilized.
So, if GNU Make had this problem solved for such a long time, why did people experiencing it keep using Ninja? Well, unfortunately many tools like Meson did not support GNU Make. This meant developers still needed Ninja.
Over the years, this has led to a few soft forks of Ninja where the sole change was merging jobserver support (including one from Kitware, the company behind CMake).
Thankfully, as of this blog post, Ninja now supports the exact same tried-and-tested jobserver protocol. That means the problem is completely solved, right? …Right?
What’s the catch(es)?
As you might have guessed, there are multiple catches.
For one, Ninja only implements support for the jobserver client. Without a corresponding server, it cannot actually do anything. This means you either need to run your Ninja instance inside an instance of GNU Make or some other server implementation.
Another issue is that on Linux it only supports the named pipe/FIFO implementation of the jobserver. This was released with GNU Make 4.4 back in October 2022, which was only added to Debian Trixie just a few months ago, in December 2024. This means you will need an extremely recent OS. Otherwise, GNU Make’s jobserver will not be compatible with Ninja.
And finally, as of writing this post, Ninja v1.13.0 (the version containing jobserver support) is less than a week-old. It will take a while before it is included in major package repositories. Until then, you will probably have to compile it from source code or use third-party binaries.
Conclusion
While I might have been nitpicky, this is a major improvement. This will give many Ninja-based projects an immediate performance improvement with minimal required changes. I certainly cannot complain too much about that.