Not only is CMake single threaded but the generation step time scales almost quadratically* with the number of targets in some cases. Probably not what happens here but this is what forced us to move from CMake — generation times were surpassing 5 minutes, and this is not counting configuring step.

* Because in CMake there are several target properties that may be affected by the "parent" (another target which added this target as dependency), meaning that these properties need to be re-evaluated in the context of each root target dependent on this one. If I remember correctly, exact mechanism is that some properties can have generator expressions, and these expressions can reference "parent" target. Now we imagine an app which has some std lib with 1k targets, and 100 top-level executables dependent on this std lib - suddenly we have 100k target evaluations in generation step.

I'm not trying to overlook cmake's issues... However my first thought on reading about your problem is "you shouldn't do that anyway - write simple code that someone else can understand". A little complexity is sometimes needed, but if your generations times are more than 30 seconds you probably should step back and do something different anyway.

Important detail here is that the problem disappers if you use PRIVATE dependencies — but you can't do that with a heavily templated C++ codebase. Templates (and complexity they bring) are there for good reasons (primarily performance at all costs) so we did not manage to find a path forward with CMake.