Wrapper of target_link_libraries

Hi,
I’m working on a large project that has managed its external library dependencies in a single “libraries.cmake” file so far. For each external dependency there is a find_package call. Now I want to change it to a more modular design where every library knows it’s own dependency to get rid of the master declaration.

For this I wrote a wrapper for target_link_libraries and introduced symbols like “EXTERNAL_LIB_{NAME}” where “{NAME}” is the Libray internal name (e.g. PNG).

A sample target_link_libraries_wrapper call looks like:
target_link_libraries_wrapper(my_libA EXTERNAL_LIB_PNG)

The wrapper evaluates the “EXTERNAL_LIB_” and calls for the given {NAME} a macro “USE_EXTERNAL_LIB_{NAME}”. In this example USE_EXTERNAL_LIB_PNG. These macros then do the work of find_package, include_directories and target_link_libraries call.

This works well for most of my libraries. But there are some where the transitive include_directories do not work.

A library my_libB which is linked to my_libA do not get the my_libA include_directories (e.g. for library PNG) and therefore I get compilation errors. This was not the case with the master declaration. Even if i change the master declaration to use the new USE_EXTERNAL_LIB_PNG macro instead of the direct find_package it works.

Now to my questions:

  • Is this a good practice to have a more modular design?
  • Is it ok to have such a “late” binding?
  • How can I do it better?
  • Why do I get these compilation errors to unknown includes?

Bests,
Michael

I think instead of working with multiple target_* commands, you should use find_package() and use IMPORTED targets. That will save you a lot of calls and variable shuffling.

I suspect that your main issue is that everything is using PRIVATE visibility. Instead, each usage should have an associated visibility:

target_link_libraries_wrapper(my_libA
  PUBLIC EXTERNAL_LIB_PNG)

Another thing you can do is make your own targets that “encapsulate” each external dependency:

find_package(PNG REQUIRED)
add_library(external_png INTERFACE)
target_link_libraries(external_png INTERFACE PNG::PNG)

# …
target_link_libraries(my_libA PUBLIC external_png)

which wouldn’t require any wrapper.

Interesting approach to have a dedicated encapsulated external dependency.

Sadly this would not solve the problem to run the find_package for a external library only when there is a target which links to it. In our project it’s possible to have derivatives which do not need to have find_package calls to libraries they don’t use, but others do.

For this reason a late dependency tracking would be nice.

Bests and a nice weekend
Michael

IMPORTED targets are scoped, but your own add_library is not, so the add_library version can be accessed from anywhere once made without another find_package call.

Ok yes, but I need a find_package call even if the library is not linked from any target. Or am I misunderstand you.

You can do find_package when the target is needed and ignore it all if it ends up being unnecessary.

Hi Ben,

I solved the problem with a combined approach. I use your proposal about to have an interface library with my symbolic name scheme. Now I do have a late binding with all advantages of a interface libraries. If a symbolic name is never used, there is never a find_package call and no interface library is built.

Have many thanks for your help.

Bests,
Michael