I switched to samurai for the few things I have that still used ninja; it's an improvement in every possible way.
But regardless, I think those kinds of build systems are just wrong. What I want from a build system is to hash the content of all the transitive inputs and look up if it exists or not in a registry.
Any build system is overly generic and it's up to the user to define how things should be built. So what happens is that at the end of the day every project ends up with a poorly made build system layered on top of a third-party generic tool but without abstracting away its complexity or abstractions.
My opinion is that a build system should figure out on its own how to build files, that is its job. The last thing I want to do is to define targets or dependencies. All of this is already implicit from the code itself and is useless busywork. I should just point it to a file or directory and that is it.
I prefer to just build my own build systems, bespoke to each project or environment, that just does what it should, no more and no less, leveraging the conventions in place and neatly integrating with team workflows (debugging, sanitizers, continuous integration, release, packaging, deployment, etc.)
I find that when you do that, there isn't much value in using any of the tools, they just add noise or make things slow. Running a graph of compiler and linker commands in parallel is fairly trivial and can be done in 20 lines of Python. The hard part is figuring out where the dependencies live, which versions to pick, and how the code implies those dependencies; for which the tools do nothing.
The problem with handcrafted build system is only the author can effectively maintain it. When he moves on, someone has to spend the time ripping it out and replace with something more standard.
I've been on both end of this situation and would rather not do it again, so I'll use whatever is the de-facto standard, but you do you.
Any project effectively has a handcrafted build system, whether it's built on top of CMake, Bazel, Scons or built from scratch doesn't really affect that.
And if it's doing everything from scratch, it's more likely to be simple and self-contained, making it easier to maintain.
I've built something similar, a Deno library called "TDAR"[1], and it works well, but it takes some work to wrap up all the command-line tools that expect to work in some mutable filesystem so that you can pretend you're calling pure functions.
[1] I haven't got around to pulling it out of the parent project[2], but I talked about it in this youtube video: https://youtu.be/sty29o8sUKI
[2] If you're interested in this kind of thing you could poke me to open up the source for that thing. togos zero zero at gee mail dot comb
One thing in particular that's always been a problem with Ninja is the output. It does too much buffering, removes colors without being able to force them back, and in general leads to an experience where for me it's not usable since I want to pipe its output to a pager. When I used ninja I needed to maintain builds with all sorts of patches to fix it. With samurai it just did the right thing out of the box.
But regardless, I think those kinds of build systems are just wrong. What I want from a build system is to hash the content of all the transitive inputs and look up if it exists or not in a registry.