Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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!


See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
110 views
Welcome To Ask or Share your Answers For Others

1 Answer

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...