waf continues to be the underappreciated gem in this space: high level build language supporting many languages and tools, cross platform, implemented and extensible in Python, nothing to install (except Python), and fast.
It is quite fast and Python is a nice language for specifying the build, but I found waf to be difficult to extend.
I had some tests that needed a binary file. That binary file was generated from a text file by a utility program. That utility program had to be built from source.
After a week of trying to get that correctly specified, I gave up. I thought I'd found the right way of doing it several times, but each time I discovered the dependency chain was broken in some way. I looked into how official modules that dealt with generated files were implemented, but I found them to be surprisingly complex.
By comparison, it was only mildly annoying to do that sort of thing in CMake (the build system I moved to after a year or two with waf). The relative simplicity of CMake was what convinced me to switch.
That being said, it's been several years since I last used waf. It may have improved during that time.
I have found the learning curve to be very steep. Extension points are several, subtle, and powerful, leveraging heavily not only normal OOP but also python meta programming. It is not out of the question go beyond all that and monkey patch some core class; I’ve seen this recommended by maintainers in some circumstances.
The docs are up front that waf is not a build system but a build system toolkit. For example, there is a demo that uses makefiles as the build language and runs them in waf. Another demo requires no build file but just finds the source files and builds them. But I have yet to see any external project released that says, “This is a build system... built on the waf build system toolkit,” like the Software Construction Toolkit was built on scons. Perhaps the toolkit is so close to being a build system that people just use it as is and require only small customizations, which are kept in their projects.
I had some tests that needed a binary file. That binary file was generated from a text file by a utility program. That utility program had to be built from source.
After a week of trying to get that correctly specified, I gave up. I thought I'd found the right way of doing it several times, but each time I discovered the dependency chain was broken in some way. I looked into how official modules that dealt with generated files were implemented, but I found them to be surprisingly complex.
By comparison, it was only mildly annoying to do that sort of thing in CMake (the build system I moved to after a year or two with waf). The relative simplicity of CMake was what convinced me to switch.
That being said, it's been several years since I last used waf. It may have improved during that time.