It is very rarely known, but I have to vouch for Mill. It is a very well thought out generic build tool written in scala - but it is pretty much as simple as a build tool can be, but not simpler. That is, it does parallelize tasks and properly caches.
You basically just write any imperative code, and if you refer to your other functions from within, they will automagically be added as an input for that function (this is possible through scala metaprogramming magic, but you are never exposed to that).
The output of your function will be serialized and cached. That’s it.