|
|
## Introduction
|
|
|
|
|
|
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()`](http://www.cmake.org/cmake/help/v2.8.10/cmake.html#command: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 a very simple
|
|
|
project usable for other CMake-based projects.
|
|
|
|
|
|
The full example is available as a download in the zip-file
|
|
|
[FooBar.zip](/uploads/a91192d30ee2df45bd225b08c3a20c1d/FooBar.zip).
|
|
|
|
|
|
### 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
|
|
|
|-- FooBarConfigVersion.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.
|
|
|
|
|
|
``` 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")
|
|
|
if(WIN32 AND NOT CYGWIN)
|
|
|
set(DEF_INSTALL_CMAKE_DIR CMake)
|
|
|
else()
|
|
|
set(DEF_INSTALL_CMAKE_DIR lib/CMake/FooBar)
|
|
|
endif()
|
|
|
set(INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH
|
|
|
"Installation directory for CMake files")
|
|
|
|
|
|
# Make relative paths absolute (needed later on)
|
|
|
foreach(p LIB BIN INCLUDE CMAKE)
|
|
|
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}/FooBarTargets.cmake")
|
|
|
|
|
|
# Export the package for use from the build-tree
|
|
|
# (this registers the build-tree with a global CMake-registry)
|
|
|
export(PACKAGE FooBar)
|
|
|
|
|
|
# Create the FooBarConfig.cmake and FooBarConfigVersion files
|
|
|
file(RELATIVE_PATH REL_INCLUDE_DIR "${INSTALL_CMAKE_DIR}"
|
|
|
"${INSTALL_INCLUDE_DIR}")
|
|
|
# ... for the build tree
|
|
|
set(CONF_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}" "${PROJECT_BINARY_DIR}")
|
|
|
configure_file(FooBarConfig.cmake.in
|
|
|
"${PROJECT_BINARY_DIR}/FooBarConfig.cmake" @ONLY)
|
|
|
# ... for the install tree
|
|
|
set(CONF_INCLUDE_DIRS "\${FOOBAR_CMAKE_DIR}/${REL_INCLUDE_DIR}")
|
|
|
configure_file(FooBarConfig.cmake.in
|
|
|
"${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/FooBarConfig.cmake" @ONLY)
|
|
|
# ... for both
|
|
|
configure_file(FooBarConfigVersion.cmake.in
|
|
|
"${PROJECT_BINARY_DIR}/FooBarConfigVersion.cmake" @ONLY)
|
|
|
|
|
|
# Install the FooBarConfig.cmake and FooBarConfigVersion.cmake
|
|
|
install(FILES
|
|
|
"${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/FooBarConfig.cmake"
|
|
|
"${PROJECT_BINARY_DIR}/FooBarConfigVersion.cmake"
|
|
|
DESTINATION "${INSTALL_CMAKE_DIR}" COMPONENT dev)
|
|
|
|
|
|
# Install the export set for use with the install-tree
|
|
|
install(EXPORT FooBarTargets DESTINATION
|
|
|
"${INSTALL_CMAKE_DIR}" COMPONENT dev)
|
|
|
```
|
|
|
|
|
|
### The files `FooBar/{foo,bar}/CMakeLists.txt`
|
|
|
|
|
|
The file `FooBar/foo/CMakeLists.txt` is pretty simple and looks like
|
|
|
expected:
|
|
|
|
|
|
``` cmake
|
|
|
configure_file(config.h.in "${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY)
|
|
|
|
|
|
add_library(foo SHARED foo.c foo.h config.h.in)
|
|
|
|
|
|
set_target_properties(foo PROPERTIES
|
|
|
PUBLIC_HEADER "foo.h;${CMAKE_CURRENT_BINARY_DIR}/config.h")
|
|
|
|
|
|
install(TARGETS foo
|
|
|
# IMPORTANT: Add the foo library to the "export-set"
|
|
|
EXPORT FooBarTargets
|
|
|
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin
|
|
|
LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT shlib
|
|
|
PUBLIC_HEADER DESTINATION "${INSTALL_INCLUDE_DIR}/foo"
|
|
|
COMPONENT dev)
|
|
|
```
|
|
|
|
|
|
The file `FooBar/bar/CMakeLists.txt` is even shorter:
|
|
|
|
|
|
``` cmake
|
|
|
add_executable(bar bar.c)
|
|
|
|
|
|
target_link_libraries(bar foo)
|
|
|
|
|
|
install(TARGETS bar
|
|
|
# IMPORTANT: Add the bar executable to the "export-set"
|
|
|
EXPORT FooBarTargets
|
|
|
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin)
|
|
|
```
|
|
|
|
|
|
### 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:
|
|
|
|
|
|
``` cmake
|
|
|
# - Config file for the FooBar package
|
|
|
# It defines the following variables
|
|
|
# FOOBAR_INCLUDE_DIRS - include directories for FooBar
|
|
|
# FOOBAR_LIBRARIES - libraries to link against
|
|
|
# FOOBAR_EXECUTABLE - the bar executable
|
|
|
|
|
|
# Compute paths
|
|
|
get_filename_component(FOOBAR_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
|
|
set(FOOBAR_INCLUDE_DIRS "@CONF_INCLUDE_DIRS@")
|
|
|
|
|
|
# Our library dependencies (contains definitions for IMPORTED targets)
|
|
|
if(NOT TARGET foo AND NOT FooBar_BINARY_DIR)
|
|
|
include("${FOOBAR_CMAKE_DIR}/FooBarTargets.cmake")
|
|
|
endif()
|
|
|
|
|
|
# These are IMPORTED targets created by FooBarTargets.cmake
|
|
|
set(FOOBAR_LIBRARIES foo)
|
|
|
set(FOOBAR_EXECUTABLE bar)
|
|
|
```
|
|
|
|
|
|
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 location of the `FooBarUse.cmake` file.
|
|
|
|
|
|
### The `FooBar/FooBarConfigVersion.cmake.in` file
|
|
|
|
|
|
The last file to discuss is the `FooBar/FooBarConfigVersion.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:
|
|
|
|
|
|
``` 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()
|
|
|
```
|
|
|
|
|
|
### Closing Remarks
|
|
|
|
|
|
In the above approach the `FooBar/FooBarConfig.cmake.in` file uses a
|
|
|
relative path to locate the include directory. This has the advantage of
|
|
|
making the installed package *relocatable*, i.e. the user can relocate
|
|
|
the whole installation tree and as long as the relative paths remain the
|
|
|
same, the package will continue to work with no need for manual tweaking
|
|
|
of the installed files. However, the approach also has a serious
|
|
|
drawback: On Windows it requires that the variables
|
|
|
`INSTALL_INCLUDE_DIR` and `INSTALL_CMAKE_DIR` are set to paths with the
|
|
|
same drive letter. Otherwise there is no valid relative path connecting
|
|
|
the two. If such an installation is a realistic scenario for your
|
|
|
project, you might need to hard-code the full `INSTALL_INCLUDE_DIR` path
|
|
|
when configuring `FooBar/FooBarConfig.cmake.in` at the cost of your
|
|
|
package being no longer relocatable.
|
|
|
|
|
|
----
|
|
|
This page was initially populated by conversion from its [original location](https://public.kitware.com/Wiki/CMake/Tutorials/How_to_create_a_ProjectConfig.cmake_file) in another wiki. |