> I mean, that's the whole point of cmake, ninja, etc. They'll build all of those projects, complete with testing and other ancillary projects. You don't have to be an "build system expert".

Every company I've worked for has had a new an unique variation of which ones they use and how. Most recently, I've started submitting the occasional fix upstream for genie, a premake fork. They're a horrifically leaky abstraction though, and invariably I need to fix them, extend them, or partially sidestep them. By the time you've gotten up to speed on two or three of them well enough to effectively manage all that configuration, you've probably become a build system expert, if only to debug when they fall over. I guess you don't technically need to be one though?

> And you can absolutely make your build times better than what you're citing,

I mean, yes. It's an ongoing battle. I enumerated one of many improvements - breaking up your configuration dependencies.

> coming from someone with binaries measured in tens of megs with templates out the wazoo.

Typically I'm dealing with gigs of build outputs (tens of gigs including temps like .obj files) per individual cell of a rather large CI build matrix. My current job is no exception.

> Just break up your dependencies.

Trust me, I get it. I'm currently dealing with several hundred separately built projects. Coworkers are happy that VS2017 can load our .sln s faster, and we have our project generator able to select subsets of that in dozens of different ways.

But I work on a large C++ codebases with macros. If I tweak something truly and fundamentally global - the configuration of if and how our assertion macros should expand, for example - no amount of "breaking up dependencies" is going to fix anything. I will have invalidated everything. The build server will rebuild everything, because that's a dependency in everything. Those macros are used in a large enough subset of TUs that we toss it in a precompiled header for improved build performance. That's legitimately the right choice, and it still means rebuilding literally every single TU.

When that's configured as inherited global defines in a project generator, as they almost always are, and people add their own configuration defines there, as they almost always do to "keep things in one place" and all that, suddenly tweaking configuration options that are used in one place rebuild the entire tree. So I go to fix that when I find myself needing to tweak them.

Say you have a network desync check assertion or debugging macro exposed by a networking module (we'll imaginatively call it 'network'.) Where do you drive the #define s configuring when it's removed?

In the generator config for the network project's private defines? No, this is a public macro, stripping it from the consumers requires they see the config too.

In the generator config for the network project's public/inherited defines? Touching that now means rebuilding everything that uses the network module, which might be a lot more widely used than the macro.

In the generator config of each project that uses the macro, so they can specify which file(s) to make the configuration defines in? That's incredibly brittle and an invite to merge hell and a lot of duplicate configuration spread all over.

In config_network_desync_checks.h? You can #include it from just the header that defines the macro. Tweaking it will only rebuild the TUs that actually depend on that file. You can still put them in "one place" (e.g. a central directory) if you want and still retain those advantages. You don't have to rerun your project generator, wait for it to rebuild everything, click through Visual Studio prompting you to let it reload the project, watch it hang or occasionally crash as it reparses things... you can just edit, let your build system recompile a few TUs, relink as needed, and then just run.