How to write cmake modules for "boost-like" multi-component library?
Asked Answered
C

1

8

I'm currently writing a c++ library, that has several "sub libraries", like for example the boost library.

Let's name the library "TestLib" and the sub libraries "Base" and "Ext", where Base contains come basic stuff, that doesn't depend an any other sub library. Ext however depends on some classes of Base.

Each "sub library" should compile into a separate .a or .so file, but they all should share a namespace (TestLib). Now i'm aiming to write clean cmake scripts in order to achieve this goal.

In the end i want to be able to do something like this in cmake:

find_package(TestLib 0.1 REQUIRED COMPONENTS Base Ext)

or

target_link_libraries(someapplication
PUBLIC
    TestLib::Base
)

I have put each "sub library" in a separate git repository and added them as submodules in a new repository that has only a CMakeLists.txt that just calls add_subdirectory on each of the repos.

Most of the cmake stuff i achieved, i got from this awesome tutorial at https://pabloariasal.github.io/

And the Base part works as intended (which is no wonder, since it doesn't depend on anything else).

But my problems come with the Ext part. In order for this to compile, i have to link against the Base library, which shouldn't be to hard, and with some trial and error i am sure that i will get it to work.

But i want to do it the right way.

My approach was to

find_package(TestLib COMPONENTS Base)

in the CMakeLists.txt of TestLib.Ext. But this cant be found since it has no TestLibConfig.cmake.

Which makes sense, but i don't know what to put in this file.

I tried to provide some code that exactly describes my problem, but since this would be too much to post here, i created a github for this purpose:

https://github.com/PowerSupplyTopologies/TestLib

This should contain all the relevant code.

This could be trivial to some of you, but i bet there are more people that could benefit from this approach.

Thank you in advance for any of your thoughts.

EDIT:

The library creation in the CMakeLists.txt of Base is:

set(TARGET_NAME testlibbase)

add_library(${TARGET_NAME}
    src/ClassA.cpp
    src/ClassB.cpp
)

#Add an alias so that library can be used inside the build tree, e.g.     when testing
add_library(TestLib::${TARGET_NAME} ALIAS ${TARGET_NAME})

and

set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/TestLib)

install(TARGETS ${TARGET_NAME}
    EXPORT ${TARGET_NAME}-targets
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)

#This is required so that the exported target has the name JSONUtils and not  jsonutils
set_target_properties(${TARGET_NAME} PROPERTIES EXPORT_NAME Base)

install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

#Export the targets to a script
install(EXPORT ${TARGET_NAME}-targets
  FILE
    TestLibBaseTargets.cmake
  NAMESPACE
    TestLib::
  DESTINATION
    ${INSTALL_CONFIGDIR}
)

#Create a ConfigVersion.cmake file
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
    ${CMAKE_CURRENT_BINARY_DIR}/TestLibBaseConfigVersion.cmake
    VERSION ${PROJECT_VERSION}
    COMPATIBILITY AnyNewerVersion
)

configure_package_config_file(${CMAKE_CURRENT_LIST_DIR}/cmake  /TestLibBaseConfig.cmake.in
    ${CMAKE_CURRENT_BINARY_DIR}/TestLibBaseConfig.cmake
    INSTALL_DESTINATION ${INSTALL_CONFIGDIR}
)

#Install the config, configversion and custom find modules
install(FILES
    ${CMAKE_CURRENT_BINARY_DIR}/TestLibBaseConfig.cmake
    ${CMAKE_CURRENT_BINARY_DIR}/TestLibBaseConfigVersion.cmake
    DESTINATION ${INSTALL_CONFIGDIR}
)

##############################################
## Exporting from the build tree

export(EXPORT ${TARGET_NAME}-targets FILE ${CMAKE_CURRENT_BINARY_DIR}/TestLibBaseTargets.cmake NAMESPACE TestLib::)

and of Ext:

set(TARGET_NAME testlibext)

add_library(${TARGET_NAME}
    src/ClassC.cpp
)

#Add an alias so that library can be used inside the build tree, e.g. when   testing
add_library(TestLib::${TARGET_NAME} ALIAS ${TARGET_NAME})
Cannae answered 29/4, 2019 at 14:18 Comment(3)
"I tried to provide some code that exactly describes my problem, but since this would be too much to post here, i created a github for this purpose" - Link to the repo is good, but it doesn't replace the needs to have at least some code in the question post. Please, into the question post add the code which creates library (with add_library), which creates .cmake files and installs them.Livesay
I hope this helps? Sorry for not posting the code, but i don't exactly know what part is helpful for you.Cannae
Yes, this is sufficient.Livesay
G
1

In your meta project TestLib you can create a TestLibConfig.cmake file like described in the CMake documentation.

TestLibConfig.cmake:

set(_supported_components Base Ext)

foreach(_comp ${Test_FIND_COMPONENTS})
  if (NOT ";${_supported_components};" MATCHES _comp)
    set(TestLib_FOUND False)
    set(TestLib_NOT_FOUND_MESSAGE "Unsupported component: ${_comp}")
  endif()
  include("${CMAKE_CURRENT_LIST_DIR}/TestLib${_comp}Targets.cmake")
endforeach()

ref: https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#creating-a-package-configuration-file

Grisly answered 30/4, 2019 at 9:25 Comment(3)
I created a TestLibConfig.cmake in the meta project as you suggested and added a find_dependency(TestLib COMPONENTS Base) in the CMakeLists.txt of Ext. But it still can not find TestLibConfig.cmake. I updated the github.Cannae
How can i tell cmake to look for the TestLibConfig.cmake in the base directory or a subdirectory of it?Cannae
It works now thanks to your suggestion. But one thing still isn't quite clear to me: When i reference one lib from the same package, i cant use find_package or find_dependency, since the package will only be visible after installing the package. What i did now is to use ${TestLibBase_SOURCE_DIR}/include in the target_include_directories. This seems wrong to me. Is that a correct way to do it or is there a better way?Cannae

© 2022 - 2024 — McMap. All rights reserved.