Project modularization using object libraries

Object libraries have some differences to static libraries which can catch people out.

  • They only add their objects to things that link to them directly. Usage requirements are carried transitively to consumers (meaning things like PUBLIC compile flags and header search paths can propagate to consumers of consumers), but the objects themselves do not. A relatively common query we see in the forums and in CMake’s issue tracker is people mistakenly expecting objects to be added transitively. In comparison, static libraries work intuitively when linked through multiple levels of dependencies. CMake will propagate linking them as needed, even when something links to them as PRIVATE (static libraries need to propagate their used symbols up to the shared library, module library or executable).
  • Adding objects to a shared library, module library or executable has a different effect to linking a static library into a shared library, module library or executable. When linking a static library, the linker will only need to go looking in the static library for symbols it has not yet been able to resolve. If nothing uses a symbol, that symbol won’t be added to the binary from the static library (unless you add linker flags to specifically ask for that behavior, which should be rare). Adding objects is different, it puts all the objects’ symbols into the linker’s “need to resolve these” bucket. Depending on linker flags, the symbols provided by objects might not be discarded and therefore can still be present in the final binary, even if nothing actually uses them. I’ve seen projects take advantage of this to add code which is useful when run from within gdb, but not otherwise executed by anything in the program (think: pretty-printing custom types).

There are probably more reasons I’ve temporarily forgotten, but those are the two main ones that come to mind.