CMake and order dependent linking of shared libraries
Asked Answered
A

2

46

I have a few small components that I am building as shared libraries for my main application. Lets use an example of liba and libb. Each is built within their own subdirectory as follows:

add_library(liba SHARED a.cpp)

Then, in the root project folder, I need to link my main application to both.

include_directories(a)
include_directories(b)
add_executable(dummy dummy.cpp)
target_link_libraries(dummy a b)

CMake runs fine with this, and my application compiles but fails to link. The problem is that b references a. If I supply the order of the libraries while linking as

target_link_libraries(dummy b a)

The program compiles and links just fine

When this sort of system starts involving more complex inter dependency of the libraries, it starts to be impossible even if the dependencies are acyclic. How do I manage the linking step here? Is there a trick to ordering libraries for linking in CMake?

Albaugh answered 30/8, 2012 at 20:18 Comment(0)
A
44

You can specify the relationship between a and b by adding

target_link_libraries(b a)


From the docs:

Library dependencies are transitive by default. When this target is linked into another target then the libraries linked to this target will appear on the link line for the other target too.

So, if you specify a as a dependency of b in this way, you don't even need to explicitly list a in any target which depends on b, i.e. your other command can be just:

target_link_libraries(dummy b)

although it wouldn't do any harm to list a as well.

Antifouling answered 30/8, 2012 at 20:28 Comment(4)
What if b is an external library not compiled by the current project? (e.g. I want to link my project against jemalloc which depends on pthread.) If I use target_link_libraries(jemalloc pthread) I get the following error: Cannot specify link libraries for target "jemalloc" which is not built by this project.Fanjet
@PaulBaltescu The first parameter has to be a proper CMake target built by the current project. I don't think there's a way to specify dependency between 2 external libs like this. The best you can probably do is something like find_library for each, and with the results (assuming they're both found correctly) do set(JemallocLibs ${JemallocLib} ${PthreadLib}). Then you can just do e.g. target_link_libraries(MyLib ${JemallocLibs}).Antifouling
@Antifouling what about dependencies between build libraries and extern libraries, say I build GLFW (target_link_libraries(MyProj GLFW)) which depends on X11 (-lX11). I did not find a way with this kind of dependencies in cmake.Conatus
b not only has to be a library compiled by the current project, but even in the same subdirectory as a. If you have a hierarchical CMakeListstructure using add_subdirectory, you may get Attempt to link library a to target b which is not built in this directoryOpenair
S
18

An easy solution (especially for circular dependencies) can be to just put all your libraries in a list variable, then add that list twice (or more if necessary), like:

set(LINK_LIBS "liba libb libc")
target_link_libraries(app ${LINK_LIBS} ${LINK_LIBS})

(or just type out the list twice after each other in the target_link_libraries function)

This has worked for me quite a couple of times, but I'll admit that there might be some possible drawbacks that I'm unaware of (other than it seeming like a bit of a hack).

Scoville answered 30/8, 2012 at 20:37 Comment(1)
There are better solutions to circular dependencies #5652369. You should probably be more explicit about it when it arises. I've also found in my own compilation that I misunderstood link order and believed there were recursive dependencies that weren't recursive, just because I linked in the wrong order.Portuna

© 2022 - 2024 — McMap. All rights reserved.