May want to look at Just. It is heavily inspired by Make and shares much of the same syntax, but removes a lot of the workarounds necessary to use Make as a task runner and adds a few other features.
If Just grew to have a large enough feature-set that it could replace GNU Make, it would collect an equal amount of warts and sharp-edges along the way.
It's a little more user-friendly, but not enough to justify how much power you lose.
I spent the last 20 minutes reading Just's documentation, and it seems like the only real wins are nicer argument parsing and && dependencies, and the --list argument. And in exchange for that, you lose file-based dependendicies and Make's powerful templating engine. Did I miss some other features that make the trade worth it?
File dependencies are useful as part of a build system.
But often tasks are a step removed from that. Targets like "all" and "clean" are good examples of useful tasks to have, even if they're unrelated to files.
A "task runner" isn't intended to replace all of a Makefile's functionality. It takes a common use case of Makefiles, and improves the user experience for this.
I think it's fair to say that it's not a big step up; but it's a definite improvement for what it does aim to do.
Targets don't need to produce files in Make (see: your examples of 'all' and 'clean'). And non-file targets can depend on other non-file targets. Make does this out of the box, by default.
But if you ever get to a point where you _want_ to express some source-code dependencies (say: pip-install depends on requirements.txt), you can also do that in Make. Just doesn't provide any mechanism for it.
If you don't want them, don't use them. Make doesn't need you to produce actual files called 'clean', 'build', etc. you don't even have to do anything special unless your build folder has actual files called those things (in which case, you could declare them as phonies).
From Make's point of view, they're just target names. Make doesn't care whether or not a target produces any files. Targets can satisfy dependencies for other targets, as can files on the filesystem.
There are so many things where that’s not relevant. “Just start” or “just test” or “just install” are idempotent, or at least stateless (assuming “start” calls out to systemctl or such).
That's all true, but understates the important of a nicer UX. I've spent lots of time over the years working around Makefiles' idiosyncrasies. It's so good at its intended purpose that it's tempting to use it for vaguely related tasks. Turns out it's not as good for doing those things it was never designed for.
"Just" reimagines what make could look like if it were designed to be a task runner instead. That hits a sweet spot for many of us. It looks like the Makefiles we're used to, minus the pains in the neck we've tolerated from abusing make for our own purposes.
https://github.com/casey/just