Idiomatic way to deal with huge number of include directories


I’m writing a CMake build system for a very large, very elderly code base. It contains tens of thousands of source code files, most of which live in a complex nested directory structure. Each target is likely to have tens (if not hundreds) of subdirectories that contain source code.

My fundamental question is, what’s the idiomatic CMake way to deal with this?

What I’m attempting at the moment is, for each target, adding the subdirs as PUBLIC target_include_directories. This has the following problems:

  • Sometimes, libraries wish to consume only headers from a library. The cleanest way to manage “inheritance” of include directories seems to be to add a link dependency between the two libraries. Adding a link just to get some includes seems inelegant… Is there a better way to handle inheritance of includes?
  • The list of header files is so large that when developers write a bug, it’s normally impossible to see the first error without piping the error to file and doing a lot of scrolling. Is there a way to reduce the size of the messages I get? It would be great if I could somehow collapse the list of include directories.

I’m building on Linux with Make, Ninja, Clang, and Gcc.


You can do something like this:

add_library(foo-headers INTERFACE)
target_include_directories(foo-headers INTERFACE …)
add_library(foo SHARED …)
target_link_libraries(foo PUBLIC foo-headers)

Eventually, we’ll have $<COMPILE_ONLY> to strip off the linking (there’s an issue for it, but I don’t have it handy).

I don’t know of a way to do this. Maybe you could force response files, but I think that is Ninja-only.

Thanks Ben, very useful!

I did mess around with INTERFACE libraries for a bit, but got in a tangle with the export rules. I’ll give it another go.

At the moment the only idea I have to fix the second problem is to programmatically re-factor all the #include bar.h#include path/from/top/level/target/dir/bar.h. I’ll have to think about that one a bit more though - I don’t actually think it would be that hard to script…

I think that is the better way to handle it because then you don’t have to worry about conflicting header names and the reader of the code can have a better idea of what frobnicator.h is for if it is referred to as package/processing/frobnicator.h. Other (minor) benefits:

  • fewer paths the compiler pokes around in for headers that are found after your targets
  • shorter command lines