Search code examples
c++cmakevcpkg

cmake add an optional dependency to a static library without forcing consumers to depend on its depencdencies


I have a static library built using cmake and I'm trying to integrate it to vcpkg. The library has some wrappers for things like ssl using openssl and sqlite databases but they are optional and not required to used other parts of the library. The source files looks like this:

include:
core.h
ssl.h
sql.h

src:
core.cpp
ssl.cpp
sql.cpp

the source files ssl.cpp and sql.cpp include the headers from openssl and sqlite to implement their functionality but core.cpp does not need either of them. I used vcpkg manifest features to enable any feature and I check in the cmake script to enable features on demand:

if (OPENSSL_FEATURE)
    find_package(OpenSSL REQUIRED)
    target_compile_definitions(thelib  PUBLIC HAVE_OPENSSL)
    target_link_libraries(thelib PRIVATE OpenSSL::SSL PRIVATE OpenSSL::Crypto)
endif()

Now I have another library which depends on the core part of this previous library and also built with cmake and vcpkg:

find_package(thelib  REQUIRED)
target_link_libraries(otherlib PRIVATE thelib)

but cmake is giving an error saying that thelib depends on OpenSSL::SSL and other libraries but it was not found. When I added the proper find_package to find these packages without target_link_libraries then the build passes but now consumers of otherlib will try to link to thelib and will be required to find all the packages required even it is not used by the consumer.

I thought that using PRIVATE in target_link_libraries will hide the dependencies from the consumers but it turned out that dependencies of a static library are added to the link targets even if PRIVATE is used.

The solution I'm thinking of is to split the library into several libraries which depend on each other as required but for a small library and basic things like this it is very annoying and much of work.

Does anyone know how to instruct cmake to link only the used packages ?

EDIT: To clarify more the problem is that in the thelib-target.cmake generated by installing the target and included in thelib-config.cmake there exists this cmake code:

set_target_properties(thelib::thelib PROPERTIES
  INTERFACE_COMPILE_DEFINITIONS "HAVE_OPENSSL;HAVE_SQLITE"
  INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
  INTERFACE_LINK_LIBRARIES "\$<LINK_ONLY:OpenSSL::SSL>;\$<LINK_ONLY:OpenSSL::Crypto>;\$<LINK_ONLY:SQLite::SQLite3>"
)

which requires those dependencies to be visible when linking against thelib but otherlib does not need to use target_link_libraries to link any of them but only find_package to make them visible and also the final executable result will not include the libraries it does not use because adding a library to the linker line only adds it to the linker search set and if it is not referenced by the executable it will not be included.

The problem is that consumers are required to use find_package to search for unused libraries. I see that some libraries contain many dependencies like POCO but it builds many libraries and consumer are free to link against any of them. I don't want to create many libraries. Can cmake components solve this problem ?


Solution

  • The way I got around this problem is by dividing the library into several libraries and link the required components per application demand. I am not an expert in cmake so I used the template from this repo to get it: https://github.com/ClausKlein/cmake-example-component-lib