42

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?

usr1234567
  • 21,601
  • 16
  • 108
  • 128
dusktreader
  • 3,845
  • 7
  • 30
  • 40

2 Answers2

40

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.

Fraser
  • 74,704
  • 20
  • 238
  • 215
  • 8
    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.` – Paul Baltescu Sep 15 '13 at 04:35
  • 1
    @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})`. – Fraser Sep 15 '13 at 09:19
  • 1
    @Fraser 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. – stanleyerror Dec 03 '15 at 14:12
  • `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 `CMakeList`structure using `add_subdirectory`, you may get `Attempt to link library a to target b which is not built in this directory` – MSalters Jul 15 '19 at 15:41
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).

sonicwave
  • 5,952
  • 2
  • 33
  • 49
  • There are better solutions to circular dependencies https://stackoverflow.com/questions/5651869/what-are-the-start-group-and-end-group-command-line-options. 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. – xaviersjs Jun 29 '22 at 07:40