0

I have a project where both static and shared libraries are built from the same sources. The static library uses object files built using -fPIC. For now, I have come to the conclusion this is essentially a flawed approach because of the way OBJECT libraries in CMake propagate their private dependencies, but I may be wrong. Consider the following CMake project, where libuuid is some random 3rd-party dependency to illustrate the problem:

find_package(PkgConfig REQUIRED)
pkg_check_modules(UUID REQUIRED uuid IMPORTED_TARGET)

# Object library.
add_library(foo-object OBJECT)
target_sources(foo-object PRIVATE foo.cpp foo.h)
target_link_libraries(foo-object PRIVATE PkgConfig::UUID)

# Shared library built using the object from the object library.
add_library(foo-shared SHARED)
target_link_libraries(foo-shared PUBLIC foo-object)

# Shared library without an intermediate object library.
add_library(bar-shared SHARED)
target_sources(bar-shared PRIVATE foo.cpp foo.h)
target_link_libraries(bar-shared PRIVATE PkgConfig::UUID)

# Two different executables, using either one of the shared libraries defined above.
add_executable(foo-test)
target_link_libraries(foo-test PRIVATE foo-shared)
target_sources(foo-test PRIVATE test.cpp)

add_executable(bar-test)
target_link_libraries(bar-test PRIVATE bar-shared)
target_sources(bar-test PRIVATE test.cpp)

In short; this builds two shared libraries, foo-shared and bar-shared, where foo-shared is built from an OBJECT library, and bar-shared is built directly from sources.

When linking the test binaries foo-test and bar-test, the link commands look roughly as follows:

/usr/bin/c++ test.cpp.o -o bar-test libbar-shared.so
/usr/bin/c++ test.cpp.o -o foo-test libfoo-shared.so /usr/lib/libuuid.so

So the problem here is that libuuid.so ends up in the linker command when linking libfoo-shared.so, even though it is a private dependency of foo-object. So essentially foo-object's dependencies are treated in the same way as static library dependencies. This is the dependency graph generated by CMake:

CMake dependency graph

The question now is; are object libraries a fundamentally flawed approach when building static and shared libraries from the same object files because of this issue or am I missing something? Is there another way to prevent building all sources twice when building both a static and shared library from the same objects in CMake?

Ton van den Heuvel
  • 10,157
  • 6
  • 43
  • 82
  • When consider reusing same object library in both shared and static one, take into account that on **Windows** this approach never works: object files for a shared library needs to be compiled with `declspec(export)` enabled. – Tsyvarev Jun 30 '23 at 09:12
  • `the problem here is that libuuid.so ends up in the linker command when linking libfoo-shared.so, even though it is a private dependency of foo-object` Why is that a problem? How else would you resolve the libuuid.so symbols when linking? That is great, it is needed, it should be there. I do no tunderstand your question - you ask about building static and shared library. Yet, in your code, you build _two shared_ libraries. I do not understand if you ask about transitive shared library linking options _or_ you want to `add_library(foo-static STATIC)target_link_libraries(foo-static foo-object)`? – KamilCuk Jun 30 '23 at 10:32
  • @KamilCuk, no, it is not needed, `libuuid.so` is already linking against `libfoo-shared.so`, `ldd` on that shared library will tell you that. It does not need to be listed separately when linking some binary using `libfoo-shared.so`. Imagine having to list all shared libraries some shared library links against when linking against that shared library...Btw, the example above shows you that it is not needed, since `bar-test` links correctly against `libbar-shared.so` without requiring `libuuid.so` on the linker line. – Ton van den Heuvel Jun 30 '23 at 10:56
  • `Imagine having to list all shared libraries some shared library links against when linking against that shared library` I do not understand, this is exactly what you do. `is not needed, since bar-test links correctly against libbar-shared.so without requiring libuuid.so` Yea, because you have no external inline functions that use uuid. Usually I do link with all the requirements and all requirements of requirements. I do not understand how is that a problem, and I am sorry I do not understand how is that related to static library mentioned in the post. – KamilCuk Jun 30 '23 at 11:08
  • related: https://alexreinking.com/blog/building-a-dual-shared-and-static-library-with-cmake.html and https://stackoverflow.com/q/2152077/11107541 – starball Jun 30 '23 at 22:01
  • @KamilCuk, my point is that in my opinion `bar-test` is linked correctly (by *not* specifying `/usr/lib/libuuid.so`). So I was curious what for example happens for libxml2; as it turns out that will include most second order dependencies of `libxml2.so` on the link line as well, and now I'm confused... – Ton van den Heuvel Jul 03 '23 at 06:23
  • Ok, I stand by what I posted here, and that `/usr/lib/libuuid.so` should not be listed on the link line after reading http://www.kaizou.org/2015/01/linux-libraries.html; `libuuid` is a second order dependency, and still listing it is bad as explained in the linked article (overlinking). – Ton van den Heuvel Jul 03 '23 at 06:56

0 Answers0