CMake/Tutorials/How to create a ProjectConfig.cmake file
How to create a ProjectConfig.cmakeB file
Native CMake projects that are intended to be used by other projects (e.g. libraries, but also tools that could be useful as a build-utility, such as documentation generators, wrapper generators, etc.) should provide at a minimum a <name>Config.cmake or a <lower-name>-config.cmake file. This file can then be used by the find_package() command in config-mode to provide information about include-directories, libraries and their dependencies, required compile-flags or locations of executables. You are advised to carefully read the documentation of the find_package() command before proceeding. This short article will show you how to do so for a very simple project.
The FooBar project
Let's assume the project contains a simple shared library, foo and a utility that uses the library, bar. The source tree could have the following layout:
FooBar/ |-- CMakeLists.txt |-- FooBarConfig.cmake.in |-- FooBarVersion.cmake.in |-- foo/ | |-- CMakeLists.txt | |-- config.h.in | |-- foo.h | `-- foo.c `-- bar/ |-- CMakeLists.txt `-- bar.c
The files FooBar/foo/{config.h.in,foo.h,foo.c} and FooBar/bar/bar.c are of little interest here and their contents are left to the imagination of the reader.
The main FooBar/CMakeLists.txt file
A simple FooBar/CMakeLists.txt could look like the following, where the really interesting stuff starts after the respective comment.
<source lang="cmake"> cmake_minimum_required(VERSION 2.8) project(FooBar C)
set(FOOBAR_MAJOR_VERSION 0) set(FOOBAR_MINOR_VERSION 1) set(FOOBAR_PATCH_VERSION 0) set(FOOBAR_VERSION
${FOOBAR_MAJOR_VERSION}.${FOOBAR_MINOR_VERSION}.${FOOBAR_PATCH_VERSION})
- Offer the user the choice of overriding the installation directories
set(INSTALL_LIB_DIR lib CACHE PATH "Installation directory for libraries") set(INSTALL_BIN_DIR bin CACHE PATH "Installation directory for executables") set(INSTALL_INCLUDE_DIR include CACHE PATH
"Installation directory for header files")
set(INSTALL_DATA_DIR share CACHE PATH
"Installation directory for data files")
- Make relative paths absolute (needed later on)
foreach(p LIB BIN INCLUDE DATA)
set(var INSTALL_${p}_DIR) if(NOT IS_ABSOLUTE "${${var}}") set(${var} "${CMAKE_INSTALL_PREFIX}/${${var}}") endif()
endforeach()
- set up include-directories
include_directories(
"${PROJECT_SOURCE_DIR}" # to find foo/foo.h "${PROJECT_BINARY_DIR}") # to find foo/config.h
- Add sub-directories
add_subdirectory(foo) add_subdirectory(bar)
- The interesting stuff goes here
- ===============================
- Add all targets to the build-tree export set
export(TARGETS foo bar
FILE "${PROJECT_BINARY_DIR}/FooBarLibraryDepends.cmake")
- Export the package for use from the build-tree
- (this registers the build-tree with a global CMake-registry)
export(PACKAGE FooBar)
- Create a FooBarConfig.cmake file for the use from the build tree
set(FOOBAR_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}" "${FOOBAR_BINARY_DIR}") set(FOOBAR_LIB_DIR "${PROJECT_BINARY_DIR}/foo") set(FOOBAR_CMAKE_DIR "${PROJECT_BINARY_DIR}") configure_file(FooBarConfig.cmake.in
"${PROJECT_BINARY_DIR}/FooBarConfig.cmake" @ONLY)
- Install the export set for use with the install-tree
install(EXPORT FooBarLibraryDepends DESTINATION
"${INSTALL_DATA_DIR}/FooBar/CMake" COMPONENT dev)
- Create a FooBarConfig.cmake file for the use from the install tree
- and install it
set(FOOBAR_INCLUDE_DIRS "${INSTALL_INCLUDE_DIR}") set(FOOBAR_LIB_DIR ${INSTALL_LIB_DIR}") set(FOOBAR_CMAKE_DIR "${INSTALL_DATA_DIR}/FooBar/CMake") configure_file(FooBarConfig.cmake.in
"${PROJECT_BINARY_DIR}/InstallFiles/FooBarConfig.cmake" @ONLY)
install(FILES
"${PROJECT_BINARY_DIR}/InstallFiles/FooBarConfig.cmake" DESTINATION "${FOOBAR_CMAKE_DIR} COMPONENT dev)
</source>
The files FooBar/{foo,bar}/CMakeLists.txt
The file FooBar/foo/CMakeLists.txt is pretty simple and looks like expected:
<source lang="cmake"> configure_file(config.h.in "${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY)
add_library(foo SHARED foo.c foo.h)
set_target_properties(foo PROPERTIES
PUBLIC_HEADER "foo.h")
install(TARGETS foo
# IMPORTANT: Add the foo library to the "export-set" EXPORT FooBarLibraryDependencies RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT shlib PUBLIC_HEADER_DESTINATION "${INSTALL_INCLUDE_DIR} COMPONENT dev)
</source>
The file FooBar/bar/CMakeLists.txt is even shorter:
<source lang="cmake"> add_executable(bar bar.c)
target_link_libraries(bar foo)
install(TARGETS bar
# IMPORTANT: Add the bar executable to the "export-set" EXPORT FooBarLibraryDependencies RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin)
</source>
The FooBar/FooBarConfig.cmake.in file
The really interesting file is FooBar/FooBarConfig.cmake.in. Although it usually can be quite simple, it seems to cause considerable confusion to new CMake-users. For the FooBar project the following is a plausible implementation:
<source lang="cmake">
- - Config file for the FooBar package
- It defines the following variables
- FOOBAR_INCLUDE_DIRS - include directories for FooBar
- FOOBAR_LIBRARY_DIRS - library directories for FooBar (normally not used!)
- FOOBAR_LIBRARIES - libraries to link against
- FOOBAR_EXECUTABLE - the bar executable
- Tell the user project where to find our headers and libraries
set(FOOBAR_INCLUDE_DIRS "@FOOBAR_INCLUDE_DIRS@") set(FOOBAR_LIBRARY_DIRS "@FOOBAR_LIB_DIR@")
- Our library dependencies (contains definitions for IMPORTED targets)
include("@FOOBAR_CMAKE_DIR@/FooBarLibraryDepends.cmake")
- These are IMPORTED targets created by FooBarLibraryDepends.cmake
set(FOOBAR_LIBRARIES foo) set(FOOBAR_EXECUTABLE bar) </source>
If your package also provides CMake macros or functions, you might want to put them in a file FooBarUse.cmake (or similar), install it alongside FooBarConfig.cmake and define the variable FOOBAR_USE_FILE in above code and set it to the install-tree location of the FooBarUse.cmake file.
The FooBar/FooBarVersion.cmake.in file
The last file to discuss is the FooBar/FooBarVersion.cmake.in. It is important because it allows client projects to determine the version of FooBar they found using the find_package command, but more importantly it allows the same command to automatically determine whether the detected version is suitable if the client-project requested a minimum (or even exact) version of FooBar. The file is also straightforward and usually takes the following form:
<source lang="cmake"> set(PACKAGE_VERSION "@FOOBAR_VERSION@")
- Check whether the requested PACKAGE_FIND_VERSION is compatible
if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
set(PACKAGE_VERSION_COMPATIBLE TRUE) if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") set(PACKAGE_VERSION_EXACT TRUE) endif()
endif() </source>