CMake/Tutorials/How to create a ProjectConfig.cmake file: Difference between revisions

From KitwarePublic
Jump to navigationJump to search
(Mis-named FooBarConfigVersion.cmake* and forgot to configure and install it)
(Replace content with link to new CMake community wiki)
 
(13 intermediate revisions by 4 users not shown)
Line 1: Line 1:
== Introduction ==
{{CMake/Template/Moved}}


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 <tt><name>Config.cmake</tt> or a <tt><lower-name>-config.cmake</tt> file. This file can then be used by the [http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:find_package|<tt>find_package()</tt>] 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 <tt>find_package()</tt> command before proceeding. This short article will show you how to do so for a very simple project.
This page has moved [https://gitlab.kitware.com/cmake/community/wikis/doc/tutorials/How-to-create-a-ProjectConfig.cmake-file here].
 
=== 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:
 
<pre>
FooBar/
|-- CMakeLists.txt
|-- FooBarConfig.cmake.in
|-- FooBarConfigVersion.cmake.in
|-- foo/
|  |-- CMakeLists.txt
|  |-- config.h.in
|  |-- foo.h
|  `-- foo.c
`-- bar/
    |-- CMakeLists.txt
    `-- bar.c
</pre>
 
The files <tt>FooBar/foo/{config.h.in,foo.h,foo.c}</tt> and <tt>FooBar/bar/bar.c</tt> are of little interest here and their contents are left to the imagination of the reader.
 
=== The main <tt>FooBar/CMakeLists.txt</tt> file ===
 
A simple <tt>FooBar/CMakeLists.txt</tt> 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)
configure_file(FooBarConfigVersion.cmake.in
  "${PROJECT_BINARY_DIR}/FooBarConfigVersion.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)
configure_file(FooBarConfigVersion.cmake.in
  "${PROJECT_BINARY_DIR}/InstallFiles/FooBarConfigVersion.cmake" @ONLY)
install(FILES
  "${PROJECT_BINARY_DIR}/InstallFiles/FooBarConfig.cmake"
  "${PROJECT_BINARY_DIR}/InstallFiles/FooBarConfigVersion.cmake"
  DESTINATION "${FOOBAR_CMAKE_DIR}" COMPONENT dev)
</source>
 
=== The files <tt>FooBar/{foo,bar}/CMakeLists.txt</tt> ===
 
The file <tt>FooBar/foo/CMakeLists.txt</tt> 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 FooBarLibraryDepends
  RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin
  LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT shlib
  PUBLIC_HEADER DESTINATION "${INSTALL_INCLUDE_DIR}/foo"
    COMPONENT dev)
</source>
 
The file <tt>FooBar/bar/CMakeLists.txt</tt> 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 FooBarLibraryDepends
  RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin)
</source>
 
=== The <tt>FooBar/FooBarConfig.cmake.in</tt> file ===
 
The really interesting file is <tt>FooBar/FooBarConfig.cmake.in</tt>. 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 <tt>FooBarUse.cmake</tt> (or similar), install it alongside <tt>FooBarConfig.cmake</tt> and define the variable <tt>FOOBAR_USE_FILE</tt> in above code and set it to the install-tree location of the <tt>FooBarUse.cmake</tt> file.
 
=== The <tt>FooBar/FooBarConfigVersion.cmake.in</tt> file ===
 
The last file to discuss is the <tt>FooBar/FooBarConfigVersion.cmake.in</tt>. It is important because it allows client projects to determine the version of FooBar they found using the <tt>find_package</tt> 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>

Latest revision as of 15:40, 30 April 2018


The CMake community Wiki has moved to the Kitware GitLab Instance.

This page has moved here.