Using object libraries to build a library for distribution.

Situation:

We have a complex code base with lots of smallish libraries generated by CMake. For internal use, we can just link against these libraries.

However, we also want to provide a public library for 3rd parties to use. The code in this library is implemented using many of our internal libraries. However, we only want to ship a single library. This is partly for convenience, and partly because we don’t want to expose more of our internal architecture than we need to.

Prior to moving to CMake, we had an archiving step that collapsed all the smaller libraries into a single exportable libraries. On some platforms, this was supported by the archiver (e.g. MacOS libtool). On others, we unpacked the sub-libraries and re-packed them into a single large library.

CMake doesn’t seem to have a mechanism to do this automatically. Post 3.12, one can achieve something similar with object libraries without a lot of pain. But if we switch all our internal libraries over to being object libraries, what are the hidden costs?

Alternatively, what would it take for CMake to support this behaviour directly?

Basic support would be to define a new target type ‘archive’. This would mostly function like a library, but target_link_libraries would re-pack the libraries into the archive (like happens post 3.12 when object libraries are added to a regular library) rather than add them to the link interface.

Rather than re-packing, the “archive” could treat all dependent libraries as if they were object libraries and grab their object files rather than the finished libraries. It’s possible to fudge this behaviour by using a CMake function to wrap add_library and switch between regular or object libraries. This might create issues if someone uses a feature that works on libraries but not object libraries which then breaks for archives - at least doing the switch manually rather than automatically makes it clear that someone unusual is happening.

More sophisticated archive behaviour would copy header files from PUBLIC dependencies into a new header directory. Given that adding headers (vs header paths) to projects is currently cosmetic, this could be managed in one of two ways:

  • the archive target could list (relative?) paths to all headers to be included
  • individual library targets could have some way of flagging headers as “to be exported” in the case of archives.

Handling header files might be stepping on CPack’s toes - a copy header files step is not needed to build the archive binary or link against it.

1 Like

@marc.chevrier is working on a feature that should help here by having CMake consider a “linker feature” abstraction. Given a “whole archive” feature, CMake could be coaxed into making the command line as needed. As for what features actually exist, we’ll need to figure out which CMake will provide and such as the availability across platforms is quite spotty for various features.

Thanks Ben.

I don’t fully understand the discussion on that other page, but I did notice a query about what libraries would or would not be pulled in (e.g. local vs third-party).

I’d be perfectly happy if the “archive” feature was non-transitive. That is, only explicitly specified libraries get archived, any recursive dependencies that are not archived just become part of the link interface as usual.

I’ve written up a feature request to be more precise about what I’d like for CMake to support.

The feature I am working on will not be of any help for this problem.

The goal is to decorate libraries when the linker is called, not the archiver.

OK, I’ve discovered an issue with recursive inclusion. Object libraries don’t seem to be transitive.

Consider:

CMakeLists.txt (788 Bytes)

Key lines:

target_link_libraries( b PUBLIC a )
target_link_libraries( main PUBLIC b )
target_link_libraries( main_all PUBLIC b a )

  • If B and A are regular libraries, then both main and main_all build.
  • If B and A are object libraries, then ‘main_all’ builds, but when building main, the symbols from A are missing.
  • If only one of B or A are object libraries, then both main and main_all build.

This problem exists on:

  • CMake 3.21.2 for Mac building to Makefiles
  • CMake 3.21.2 for Mac building to XCode
  • CMake 3.20.3 for Linux building to Makefiles

The problem does not seem to exist on:

  • CMake 3.20.0 for Windows building to Visual Studio 2013 or 2015

This is all with PUBLIC dependencies.

I had first thought it was an issue with PRIVATE, but the Windows builds also worked with target_link_libraries( b PRIVATE a).

I notice that 3.21 allows the use of the $<TARGET_OBJECTS:obj_lib> generator expression in target_link_libraries. When would I use this rather than just directly referencing the object library?

1 Like

Linking to OBJECT libraries only brings in they objects themselves if directly named, not if brought in transitively. This is to avoid duplicate symbols by a shared library publicly linking to an OBJECT library (for usage requirements) also putting the objects directly into a consuming executable’s link line.