1

Let's say I have a shared lib, A, and something else that links against it, B. They are in two separate projects.

In my current setup, for A's functionality to be able to work properly, B needs to add -rdynamic to its linker options, like this:

target_link_libraries(B -rdynamic)

The thing is that there may be many, many dependants of A, so having to explicitly include the line above for each of them is a hassle.

Is there a way for A to have all its dependants automatically use -rdynamic?


Edit: here's a more detailed explanation of my situation.

I have a shared library, mylib. It cannot be a static library. It is defined in a CMake project of the same name.

mylib
├── CMakeLists.txt
└── src
    └── ...

Its CMakeLists.txt looks like this:

# mylib/CMakeLists.txt
project(mylib)
...
add_library(mylib SHARED ${SOURCES})
...

In separate projects, there are executables, client_i, that use/link against mylib, sometimes indirectly (i.e. link against something that links against mylib).

client_0
├── CMakeLists.txt
└── src
    └── ...
client_1
├── CMakeLists.txt
└── src
    └── ...
client_2
├── CMakeLists.txt
└── src
    └── ...
...

One of those CMakeLists.txt looks like:

# client_i/CMakeLists.txt
project(client_i)
...
add_executable(client_i ${SOME_OTHER_SOURCES})
target_link_libraries(client_i mylib -rdynamic)
...

Notice the -rdynamic. Without this option, some functionality provided by mylib doesn't work. I need it.

My problem is that there may be thousands of different client_is, each defined in its own project by different people/users of mylib, which I provide (and may be used as a binary).

I'd like to avoid having to add -rdynamic to each of the client_is, since some users might not know about it (since it may be used indirectly), some might forget about it, it might lead to headaches, etc.

Ideally, the CMakeLists.txt for mylib would look like:

# mylib/CMakeLists.txt
project(mylib)
...
add_library(mylib SHARED ${SOURCES})
target_link_libraries(mylib -rdynamic)
...

And a client_i CMakeLists.txt would simply be:

# client_i/CMakeLists.txt
project(client_i)
...
add_executable(client_i ${SOME_OTHER_SOURCES})
target_link_libraries(client_i mylib)
...

And it would have -rdynamic in its linker options. But I have tried this and it does not seem to work.

  • 1
    Nowadays we have `target_link_options`. You can write a function that will get all dependent libraries of `A` (with like `get_target_properties(A LINK_DEPENDS ...)`) and apply `target_link_options` to each one of them. – KamilCuk Jul 15 '19 at 12:06
  • You could loop over your list of dependents, only writing the `target_link_options(B -rdynamic)` line once. – Kevin Jul 15 '19 at 12:08
  • @KamilCuk the version of CMake I have to use doesn't have `target_link_options` but I assume there's an alternative. I'm not sure how to get all dependants though. – christophebedard Jul 15 '19 at 12:20
  • https://stackoverflow.com/a/39127212/9072753 – KamilCuk Jul 15 '19 at 12:25
  • `A` and `B` are in separate projects, though. `A` doesn't know about its dependants. I'm not sure I understand! – christophebedard Jul 15 '19 at 12:30
  • What version of CMake are you using? – Kevin Jul 15 '19 at 13:20
  • @squareskittles `3.10.2` – christophebedard Jul 15 '19 at 13:25
  • 1
    I think I don't understand. Can you post some example pseudo-CMakeLists? If you have `add_library(B "")` and `target_link_libraries(B -rdynamic)` and you have `add_executable(A)` and `target_link_libraries(A B)`, then `A` will be linked with `-rdynamic` option. Because static libraries are not linked, they are just compressed, the link options interface is just passed up until executable is build. I don't know what you want. I believe your question, as now, is unclear, and should be closed. Can you add example project structure with CMakeLists and say, what you exactly want to achieve?. – KamilCuk Jul 15 '19 at 20:47
  • That's exactly what I want to achieve, but like I mentioned in my question, it has to be a shared library. It cannot be a static library. – christophebedard Jul 16 '19 at 06:48
  • @KamilCuk I've added a more in-depth explanation, including details about the overall structure. – christophebedard Jul 16 '19 at 08:24
  • The configuration you literally posted works as you indent it to do. Did you at least try it? What is your question? Please create a full MCVE. – KamilCuk Jul 16 '19 at 09:50
  • [This is an MCVE](https://gitlab.com/Kamcuk/tmp/stackoverflow-automatically-add-linking-options-to-dependants-of-shared-lib). The `-lm` is only in [mylib/CmakeListst.txt](https://gitlab.com/Kamcuk/tmp/stackoverflow-automatically-add-linking-options-to-dependants-of-shared-lib/blob/master/mylib/CMakeLists.txt), but all the `client_x` libraries are linked against it, as can be seen [here](https://gitlab.com/Kamcuk/tmp/stackoverflow-automatically-add-linking-options-to-dependants-of-shared-lib/-/jobs/252433339) `cc ... CMakeFiles/client_2.dir/main.c.o -o client_2 ... -lm -rdynamic` – KamilCuk Jul 16 '19 at 10:01
  • Note that in [here](https://gitlab.com/Kamcuk/tmp/stackoverflow-automatically-add-linking-options-to-dependants-of-shared-lib/-/jobs/252433339) when compiling clients the first `-rdynamic` option is auto-added by cmake from [Linux-GNU.cmake](https://github.com/Kitware/CMake/blob/master/Modules/Platform/Linux-GNU.cmake#L14), because cmake auto-adds it with shared libraries. The last `-rdynamic` and `-lm` are added from `mylib` module, as intended. The case is more visible when inspecting `-lm` flag. `-lm` is added only in `mylib/CMakeLists`, yet clients link against it. Case closed. – KamilCuk Jul 16 '19 at 10:02
  • My setup isn't 100% like the example I included (or your MVCE). It involves other things, so I guess my issue is somewhere in there. I don't know how to create a representative MVCE. Therefore I guess I have to close this question – christophebedard Jul 16 '19 at 13:24

2 Answers2

2

This is a simple solution you can try:

# Get a list of A's direct library dependencies.
get_target_properties(LIB_DEPENDENCIES A LINK_LIBRARIES)

# Loop through each library, adding the link option to each.
foreach(LIB ${LIB_DEPENDENCIES})
    target_compile_options(${LIB} PRIVATE "-rdynamic")
endforeach()

This can be expanded, similar to the answer @KamilCuk linked here, to account for static/shared/imported libraries and recursion depending on the variety and complexity of your dependencies.

Kevin
  • 16,549
  • 8
  • 60
  • 74
  • I'm not sure how this would fit in my use-case. `A` and `B` are in separate projects. Thus I can't do this in `A`'s `CMakeLists.txt`, since it simply cannot know about its dependants, right? Also this seems to loop over `A`'s dependencies, not its dependants. – christophebedard Jul 15 '19 at 14:07
  • Ah, dependants. I mis-understood your question. And yes, it would seem you would have to do this within the `CMakeLists.txt` for each dependant (e.g. `B`), or from your top-level `CMakeLists.txt` file. – Kevin Jul 15 '19 at 14:34
  • @christophebedard `since it simply cannot know about its dependants, right?` - but dependents know and they do this by default. So if you one of dependents will link against you, it's `LINK_LIBRARIES` will be populated using all dependencies `LINK_INTERFACE_LIBRARIES`. So, by default, it will work. What is your question then? I think you should read [cmake target_link_libraries](https://cmake.org/cmake/help/latest/command/target_link_libraries.html#command:target_link_libraries), it literally says: `Libraries for both a Target and its Dependents` – KamilCuk Jul 15 '19 at 20:51
  • If you are looking for the dependencies of `A`, then yes, `LINK_LIBRARIES` should give you what you want. But if you want a list of libraries/executables that depend on `A` (dependants), I don't think you can use this approach. The cleanest approach may be to iterate through all the targets from the top-level CMake, checking the `LINK_LIBRARIES` of each to see if `A` is present, then conditionally adding the `-rdynamic` option if necessary. Just a guess though since we don't know the hierarchy of project structure... – Kevin Jul 15 '19 at 21:26
  • Yeah, sorry, I've added more information about the project structure. It's really not monolithic. There's no top-level CMake project. – christophebedard Jul 16 '19 at 08:34
0

How to link A against B when A and B are in different projects?

If A is a different project than B, the you have to export package A and use find_package in project B to find A. Here is a good tutorial show-casing the use-case.

serkan.tuerker
  • 1,681
  • 10
  • 20