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

From KitwarePublic
Jump to navigationJump to search
(Added link to downloadable zip file containing the example)
(Replace content with link to new CMake community wiki)
 
(9 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 full example is available as a download in the zip-file [[Media:FooBar.zip|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:
 
<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}" "${PROJECT_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 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 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.