|
|
(18 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
| |
| |-- FooBarVersion.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)
| |
| | |
| # 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 <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 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 <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 FooBarLibraryDependencies
| |
| 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/FooBarVersion.cmake.in</tt> file ===
| |
| | |
| The last file to discuss is the <tt>FooBar/FooBarVersion.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>
| |