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!
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.
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.
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.
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.
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
handlesREQUIRED
,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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With