Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can one CMake target link against a *shared* version of another library target?

Tags:

c++

cmake

I have a project with a core library (LibA in the example below), an executable, and a second library (LibB) that depends on the first library. The second library (LibB) is always built in shared (dynamic library) form.

Is there any way that I can force LibB to always link against the shared version of LibA?

Here is a small CMakeLists.txt file illustrating the problem:

cmake_minimum_required(VERSION 3.16)

project(my_project LANGUAGES CXX)

# LibA should be buildable in either static or shared form.
add_library(LibA A.cpp)
# In the real case, there are many customizations to `LibA`: 
# compile flags, include dirs, target properties, etc.

add_executable(ExecutableA main.cpp)
target_link_libraries(ExecutableA LibA)

# I want library myB to *always* link against the dynamic library libLibA.so.
add_library(LibB SHARED B.cpp)
target_link_libraries(LibB PUBLIC LibA m pthread)

It can be built with the following commands:

echo 'int main() {}' > main.cpp
touch A.cpp B.cpp
mkdir -p build 
cmake -B build/ -S .
cmake --build build/

I know that I can force LibA to be always shared with add_library(LibA SHARED A.cpp), but I want to be able to build LibA as a static library.

The real context for this is that I have a core library (LibA), and want to link against it statically when creating executables, but link against it dynamically (as LibA.so) when creating a Python extension module (LibB.so).

like image 484
NicholasM Avatar asked Apr 16 '20 01:04

NicholasM


Video Answer


2 Answers

This is not possible, libA can only be either static or dynamic, not both.

You need to have two versions of libA, one dynamic, one static:

cmake_minimum_required(VERSION 3.16)

project(my_project LANGUAGES CXX)

function( addLibA suffix type )

    set( libname LibA${suffix} )
    add_library(${libname} ${type} A.cpp)

    # specify library properties here

endfunction()

# LibA should be buildable in either static or shared form.
addLibA("s" STATIC)
addLibA("" SHARED)
# In the real case, there are many customizations to `LibA`: 
# compile flags, include dirs, target properties, etc.

add_executable(ExecutableA main.cpp)
target_link_libraries(ExecutableA LibAs)

# I want library myB to *always* link against the dynamic library libLibA.so.
add_library(LibB SHARED B.cpp)
target_link_libraries(LibB PUBLIC LibA m pthread)
like image 189
jpo38 Avatar answered Oct 05 '22 10:10

jpo38


What you are specifically asking for will require multiple targets, as has been mentioned in comments / jpo38's answer

However you might be interested in using CMake's Object Libraries instead to share the objects between a library target and an executable target. For example:

cmake_minimum_required(VERSION 3.16)

project(my_project LANGUAGES CXX)

# Objects get built exactly once
add_library(LibA.objects OBJECTS 
  A.cpp
)
target_compile_commands( ... )
target_include_directories( ... )

# Shared library for libA
add_library(LibA SHARED
  $<OBJECTS:LibA.objects>
)

# LibB -- always shared, always linked against dynamic LibA
add_library(LibB SHARED
  B.cpp
)
target_link_libraries(LibB PUBLIC LibA)

# ExecutableA, built using LibA's objects 
# (effectively the same linking a static LibA.a)
add_executable(ExecutableA
  main.cpp
  $<OBJECTS:LibA.objects>
)

Using an object library, your objects get built once, but are shared between multiple targets. Using multiple library targets, on the other hand, will force a rebuild of each object, since each target may use different compile commands.

This allows ExecutableA to have LibA's objects built into it, so it behaves effectively as though you had statically linked in a LibA.a. In this case, LibA is now built as a SHARED library explicitly, since this is desired for LibB to dynamically link against.

If you're using newer versions of CMake, you can also avoid the $<OBJECTS:...> generator expression, and just link against the object library directly with target_link_libraries -- e.g.:

target_link_libraries(libA PRIVATE LibA.objects)
...
target_link_libraries(ExecutableA PRIVATE LibA.objects)

This approach will also allow properties to be applied once to their respective targets. Any properties regarding the sourcefiles themselves just needs to apply directly to the Object library. Any properties regarding the library setup/layout (e.g. output location, suffixes, etc) applies strictly to the library targets.

like image 35
Human-Compiler Avatar answered Oct 05 '22 11:10

Human-Compiler