Well, one thing I've noticed about LLVM is that it blatantly and intentionally relies on UB. The particular example I encountered probably isn't what causes the version breakage, but it's certainly a bad indicator.
That said, failures in building old software are very often due to one of:
* transitive headers (as you mentioned)
* typedef changes (`siginfo_t` vs `struct siginfo` comes to mind)
* macros with bad names (I was involved in the zlib `ON` drama)
* changes in library arrangement (the ncurses/tinfo split comes to mind, libcurl3/4 conditional ABI change, abuse of `dlopen`)
Most of these are one-line fixes if you're willing to patch the old code, which significantly increases the range of versions supported and thus reduces the number of artifacts you need to build for bootstrapping all the way to a modern version.
> zlib `ON` drama
Could you link to something about it? It's the first time I hear about it.
Rather ironic it relies on UB given the extent to which Clang + LLVM insists on interpreting UB in the most creative way possible to optimize code…