Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to configure project with COMPONENTS in cmake

I want to create a project of projects using cmake which is accessible in a similar manner to that used by Poco. I find that using Poco as an example is crowded and difficult to follow so I am trying to create a minimal version, without macros so that I can see what is going on. I have constructed a repository for this example here.

https://github.com/markeastwood82/nomnoms

This, and what is written below is currently my best guess at how to solve this problem after some days of reading up / grappling with "modern CMake", except that it doesn't quite work. Essentially I have a library noms with components fruit and veg which I want link dynamically from an application munch. I can install the noms library, but cannot find it with munch. Can someone please help me to put this thing together?

The two projects are structured are as follows:

noms
|---- CMakeLists.txt
+---- fruit
|     |---- CMakeLists.txt
|     |---- fruit-config.cmake.in
|     +---- src
|     |     |----apple.cpp
|     |
|     +---- include/noms/fruit
|           |----apple.h
|
+---- veg
      |---- CMakeLists.txt
      |---- veg-config.cmake.in
      +---- src
      |     |---- asparagus.cpp
      |
      +---- include/noms/veg
            |---- asparagus.h
munch
|---- CmakeLists.txt
+---- src
      |---- main.cpp

The file noms/CMakeLists.txt contains the following.

cmake_minimum_required(VERSION 3.0)
set(project noms)
set(version 1.0.0)

project(${project})

add_subdirectory(fruit)
add_subdirectory(veg)

# UPDATE: implement advice from Tsyvarev

configure_file(${project}-config.cmake
  "${CMAKE_BINARY_DIR}/${project}-config.cmake"
  @ONLY
)

include(CMakePackageConfigHelpers)
write_basic_package_version_file(
  "${CMAKE_BINARY_DIR}/${project}-config-version.cmake"
  VERSION ${version}
  COMPATIBILITY AnyNewerVersion
)

install(
  FILES
    "${CMAKE_BINARY_DIR}/${project}-config.cmake"
  DESTINATION lib/cmake/${project}
)

The file noms/fruit/CMakeLists.txt (and almost identical for noms/veg/CMakeLists.txt) is

set(component fruit)

add_library(${component} SHARED src/apple.cpp)

# namespaced alias
add_library(${project}::${component} ALIAS ${component})

target_include_directories(${component}
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
)

install(TARGETS ${component} EXPORT ${component}-targets
  COMPONENT ${component}
  LIBRARY DESTINATION lib
  ARCHIVE DESTINATION lib
  RUNTIME DESTINATION bin
  INCLUDES DESTINATION include
)

install(EXPORT ${component}-targets
  FILE "${project}-${component}-targets.cmake"
  NAMESPACE ${project}::
  DESTINATION lib/cmake/${project}
  COMPONENT ${component}
)

# This seems like a kludge, but it does place the file in the correct location
# on my machine (Ubuntu 18.04). Idea taken from Poco
configure_file("${component}-config.cmake.in"
  "${CMAKE_BINARY_DIR}/${project}-${component}-config.cmake"
  @ONLY
)

include(CMakePackageConfigHelpers)
write_basic_package_version_file(
  "${CMAKE_BINARY_DIR}/${project}-${component}-config-version.cmake"
  VERSION ${version}
  COMPATIBILITY AnyNewerVersion
)

install(
  FILES
    "${CMAKE_BINARY_DIR}/${project}-${component}-config.cmake"
    "${CMAKE_BINARY_DIR}/${project}-${component}-config-version.cmake"
  DESTINATION lib/cmake/${project}
  COMPONENT ${component}
)

# DESTINATION will be automatically prefixed by ${CMAKE_INSTALL_PREFIX}
install(
  DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include
  COMPONENT ${component}
  DESTINATION ${CMAKE_INSTALL_PREFIX}
)

Finally, the file for noms/fruit/fruit-config.cmake.in (and almost identical for noms/veg/veg-config.cmake) is

include(${CMAKE_CURRENT_LIST_DIR}/noms-fruit-config-version.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/noms-fruit-config-targets.cmake)

For project munch, the file munch/CMakeLists.txt is simply

cmake_minimum_required(VERSION 3.0)
set(project munch)

find_package(noms REQUIRED COMPONENTS fruit)

add_executable(${project} src/main.cpp)

target_include_directories(${project}
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:$include>
)

target_link_libraries(${project}
  PUBLIC
    noms::fruit
)

After installing noms I try to build munch, however cmake can't find the noms-config.cmake file. I don't know what it should contain, nor where I should really put it. I am also not sure it the method of using configure_file in noms/<food-type>/CMakeLists.txt is appropriate, given this does not seem to be required in the example here

https://github.com/boostcon/cppnow_presentations_2017/blob/master/05-19-2017_friday/effective_cmake__daniel_pfeifer__cppnow_05-19-2017.pdf

My question is essentially the same as was asked here, which is currently unanswered (the accepted answer is incorrect, as I have commented in that thread). Actually to be fair, since that thread is 6 years old, it may have been correct at the time of writing, but does not appear to utilize the xxx-config.cmake method that is now recommended.

How to export libraries with components in CMAKE?

Please feel free to pull me up on any mistakes I have made, or anything that could be done better. Thanks

* UPDATE *

In addition to the updated noms/CMakeLists.txt file above, I have implemented the following noms/noms-config.cmake file as per advice from @Tsyvarev below ...

foreach(component ${noms_FIND_COMPONENTS})
    include(${CMAKE_CURRENT_LIST_DIR}/noms-${component}-config.cmake)
endforeach()

The resulting code now works, thankyou!


like image 694
Mark Avatar asked Feb 15 '19 04:02

Mark


People also ask

What is a component in CMake?

cmake files. Craig Scott answer: The COMPONENT keyword allows you to group installed things into separate “bags” that you can then use to refer to them later.

What is configure in CMake?

A config-file package is a set of files provided by upstreams for downstreams to use. CMake searches in a number of locations for package configuration files, as described in the find_package() documentation.

How do I build a project using CMake?

Run the cmake executable or the cmake-gui to configure the project and then build it with your chosen build tool. Run the install step by using the install option of the cmake command (introduced in 3.15, older versions of CMake must use make install ) from the command line, or build the INSTALL target from an IDE.

Where is CMake config?

cmake the corresponding version file is located next to it and named either <config-file>-version. cmake or <config-file>Version. cmake . If no such version file is available then the configuration file is assumed to not be compatible with any requested version.


1 Answers

CMake doesn't process COMPONENTS list automatically. It leaves that mission to noms-config.cmake script, which is searched and executed when one issues the command

find_package(noms COMPONENTS fruit veg)

From the find_package documentation:

In Config mode find_package handles REQUIRED, QUIET, and [version] options automatically but leaves it to the package configuration file to handle components in a way that makes sense for the package.

Inside this config file, you may extract list of requested components (given via COMPONENTS option to find_package()) from the noms_FIND_COMPONENTS variable, and perform appropriate actions. For example:

noms-config.cmake:

foreach(component ${noms_FIND_COMPONENTS})
  # For requested component, execute its "config" script
  include(${CMAKE_CURRENT_LIST_DIR}/${component}-config.cmake)
endforeach()

This implies, that you will install the script noms-config.cmake to lib/cmake/noms subdirectory, near the other .cmake scripts you have already installed in your project.

like image 100
Tsyvarev Avatar answered Oct 17 '22 10:10

Tsyvarev