Browse Source

Merge topic 'cmake-packages-manual'

3fe4ac8 Help: Add a CMake manual for Packages related docs.
20cafa2 Split the find_dependency macro into a separate file.
Brad King 12 years ago
parent
commit
4372365177

+ 1 - 0
Help/index.rst

@@ -29,6 +29,7 @@ Reference Manuals
    /manual/cmake-generators.7
    /manual/cmake-toolchains.7
    /manual/cmake-modules.7
+   /manual/cmake-packages.7
    /manual/cmake-policies.7
    /manual/cmake-properties.7
    /manual/cmake-variables.7

+ 1 - 0
Help/manual/cmake-modules.7.rst

@@ -39,6 +39,7 @@ All Modules
    /module/CMakeDependentOption
    /module/CMakeDetermineVSServicePack
    /module/CMakeExpandImportedTargets
+   /module/CMakeFindDependencyMacro
    /module/CMakeFindFrameworks
    /module/CMakeFindPackageMode
    /module/CMakeForceCompiler

+ 414 - 0
Help/manual/cmake-packages.7.rst

@@ -0,0 +1,414 @@
+.. cmake-manual-description: CMake Packages Reference
+
+cmake-packages(7)
+*****************
+
+.. only:: html or latex
+
+   .. contents::
+
+Introduction
+============
+
+Packages provide dependency information to CMake based buildsystems.  Packages
+are found with the :command:`find_package` command.  The result of
+using ``find_package`` is either a set of :prop_tgt:`IMPORTED` targets, or
+a set of variables corresponding to build-relevant information.
+
+Using Packages
+==============
+
+CMake provides direct support for two forms of packages,
+`Config-file Packages`_ and `Find-module Packages`_.
+Indirect support for ``pkg-config`` packages is also provided via
+the :module:`FindPkgConfig` module.  In all cases, the basic form
+of :command:`find_package` calls is the same:
+
+.. code-block:: cmake
+
+  find_package(Qt4 4.7.0 REQUIRED) # CMake provides a Qt4 find-module
+  find_package(Qt5Core 5.1.0 REQUIRED) # Qt provides a Qt5 package config file.
+  find_package(LibXml2 REQUIRED) # Use pkg-config via the LibXml2 find-module
+
+In cases where it is known that a package configuration file is provided by
+upstream, and only that should be used, the ``CONFIG`` keyword may be passed
+to :command:`find_package`:
+
+.. code-block:: cmake
+
+  find_package(Qt5Core 5.1.0 CONFIG REQUIRED)
+  find_package(Qt5Gui 5.1.0 CONFIG)
+
+Similarly, the ``MODULE`` keyword says to use only a find-module:
+
+.. code-block:: cmake
+
+  find_package(Qt4 4.7.0 MODULE REQUIRED)
+
+Specifying the type of package explicitly improves the error message shown to
+the user if it is not found.
+
+Both types of packages also support specifying components of a package,
+either after the ``REQUIRED`` keyword:
+
+.. code-block:: cmake
+
+  find_package(Qt5 5.1.0 CONFIG REQUIRED Widgets Xml Sql)
+
+or as a separate ``COMPONENTS`` list:
+
+.. code-block:: cmake
+
+  find_package(Qt5 5.1.0 COMPONENTS Widgets Xml Sql)
+
+or as a separate ``OPTIONAL_COMPONENTS`` list:
+
+.. code-block:: cmake
+
+  find_package(Qt5 5.1.0 COMPONENTS Widgets
+                         OPTIONAL_COMPONENTS Xml Sql
+  )
+
+Handling of ``COMPONENTS`` and ``OPTIONAL_COMPONENTS`` is defined by the
+package.
+
+Config-file Packages
+--------------------
+
+A config-file package is a set of files provided by upstreams for downstreams
+to use. CMake searches in a number of locations for package configuration files, as
+described in the :command:`find_package` documentation.  The most simple way for
+a CMake user to tell :manual:`cmake(1)` to search in a non-standard prefix for
+a package is to set the ``CMAKE_PREFIX_PATH`` cache variable.
+
+Config-file packages are provided by upstream vendors as part of development
+packages, that is, they belong with the header files and any other files
+provided to assist downsteams in using the package.
+
+A set of variables which provide package status information are also set
+automatically when using a config-file package.  The ``<Package>_FOUND``
+variable is set to true or false, depending on whether the package was
+found.  The ``<Package>_DIR`` cache variable is set to the location of the
+package configuration file.
+
+Find-module Packages
+--------------------
+
+A find module is a file with a set of rules for finding the required pieces of
+a dependency, primarily header files and libraries.  Typically, a find module
+is needed when the upstream is not built with CMake, or is not CMake-aware
+enough to otherwise provide a package configuration file.  Unlike a package configuration
+file, it is not shipped with upstream, but is used by downstream to find the
+files by guessing locations of files with platform-specific hints.
+
+Unlike the case of an upstream-provided package configuration file, no single point
+of reference identifies the package as being found, so the ``<Package>_FOUND``
+variable is not automatically set by the :command:`find_package` command.  It
+can still be expected to be set by convention however and should be set by
+the author of the Find-module.  Similarly there is no ``<Package>_DIR`` variable,
+but each of the artifacts such as library locations and header file locations
+provide a separate cache variable.
+
+See the :manual:`cmake-developer(7)` manual for more information about creating
+Find-module files.
+
+Package Layout
+==============
+
+A config-file package consists of a `Package Configuration File`_ and
+optionally a `Package Version File`_ provided with the project distribution.
+
+Package Configuration File
+--------------------------
+
+Consider a project ``Foo`` that installs the following files::
+
+  <prefix>/include/foo-1.2/foo.h
+  <prefix>/lib/foo-1.2/libfoo.a
+
+It may also provide a CMake package configuration file::
+
+  <prefix>/lib/cmake/foo-1.2/FooConfig.cmake
+
+with content defining :prop_tgt:`IMPORTED` targets, or defining variables, such
+as::
+
+  # ...
+  # (compute PREFIX relative to file location)
+  # ...
+  set(Foo_INCLUDE_DIRS ${PREFIX}/include/foo-1.2)
+  set(Foo_LIBRARIES ${PREFIX}/lib/foo-1.2/libfoo.a)
+
+If another project wishes to use ``Foo`` it need only to locate the ``FooConfig.cmake``
+file and load it to get all the information it needs about package content
+locations.  Since the package configuration file is provided by the package
+installation it already knows all the file locations.
+
+The :command:`find_package` command may be used to search for the package
+configuration file.  This command constructs a set of installation prefixes
+and searches under each prefix in several locations.  Given the name ``Foo``,
+it looks for a file called ``FooConfig.cmake`` or ``foo-config.cmake``.
+The full set of locations is specified in the :command:`find_package` command
+documentation. One place it looks is::
+
+ <prefix>/lib/cmake/Foo*/
+
+where ``Foo*`` is a case-insensitive globbing expression.  In our example the
+globbing expression will match ``<prefix>/lib/cmake/foo-1.2`` and the package
+configuration file will be found.
+
+Once found, a package configuration file is immediately loaded.  It, together
+with a package version file, contains all the information the project needs to
+use the package.
+
+Package Version File
+--------------------
+
+When the :command:`find_package` command finds a candidate package configuration
+file it looks next to it for a version file. The version file is loaded to test
+whether the package version is an acceptable match for the version requested.
+If the version file claims compatibility the configuration file is accepted.
+Otherwise it is ignored.
+
+The name of the package version file must match that of the package configuration
+file but has either ``-version`` or ``Version`` appended to the name before
+the ``.cmake`` extension.  For example, the files::
+
+ <prefix>/lib/cmake/foo-1.3/foo-config.cmake
+ <prefix>/lib/cmake/foo-1.3/foo-config-version.cmake
+
+and::
+
+ <prefix>/lib/cmake/bar-4.2/BarConfig.cmake
+ <prefix>/lib/cmake/bar-4.2/BarConfigVersion.cmake
+
+are each pairs of package configuration files and corresponding package version
+files.
+
+When the :command:`find_package` command loads a version file it first sets the
+following variables:
+
+``PACKAGE_FIND_NAME``
+ The <package> name
+
+``PACKAGE_FIND_VERSION``
+ Full requested version string
+
+``PACKAGE_FIND_VERSION_MAJOR``
+ Major version if requested, else 0
+
+``PACKAGE_FIND_VERSION_MINOR``
+ Minor version if requested, else 0
+
+``PACKAGE_FIND_VERSION_PATCH``
+ Patch version if requested, else 0
+
+``PACKAGE_FIND_VERSION_TWEAK``
+ Tweak version if requested, else 0
+
+``PACKAGE_FIND_VERSION_COUNT``
+ Number of version components, 0 to 4
+
+The version file must use these variables to check whether it is compatible or
+an exact match for the requested version and set the following variables with
+results:
+
+``PACKAGE_VERSION``
+ Full provided version string
+
+``PACKAGE_VERSION_EXACT``
+ True if version is exact match
+
+``PACKAGE_VERSION_COMPATIBLE``
+ True if version is compatible
+
+``PACKAGE_VERSION_UNSUITABLE``
+ True if unsuitable as any version
+
+Version files are loaded in a nested scope so they are free to set any variables
+they wish as part of their computation. The find_package command wipes out the
+scope when the version file has completed and it has checked the output
+variables. When the version file claims to be an acceptable match for the
+requested version the find_package command sets the following variables for
+use by the project:
+
+``<package>_VERSION``
+ Full provided version string
+
+``<package>_VERSION_MAJOR``
+ Major version if provided, else 0
+
+``<package>_VERSION_MINOR``
+ Minor version if provided, else 0
+
+``<package>_VERSION_PATCH``
+ Patch version if provided, else 0
+
+``<package>_VERSION_TWEAK``
+ Tweak version if provided, else 0
+
+``<package>_VERSION_COUNT``
+ Number of version components, 0 to 4
+
+The variables report the version of the package that was actually found.
+The ``<package>`` part of their name matches the argument given to the
+:command:`find_package` command.
+
+Creating Packages
+=================
+
+Usually, the upstream depends on CMake itself and can use some CMake facilities
+for creating the package files. Consider an upstream which provides a single
+shared library:
+
+.. code-block:: cmake
+
+  project(UpstreamLib)
+
+  set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+  set(Upstream_VERSION 3.4.1)
+
+  include(GenerateExportHeader)
+
+  add_library(ClimbingStats SHARED climbingstats.cpp)
+  generate_export_header(ClimbingStats)
+
+  install(TARGETS ClimbingStats EXPORT ClimbingStatsTargets
+    LIBRARY DESTINATION lib
+    ARCHIVE DESTINATION lib
+    RUNTIME DESTINATION bin
+    INCLUDES DESTINATION include
+  )
+  install(
+    FILES
+      climbingstats.h
+      "${CMAKE_CURRENT_BINARY_DIR}/climbingstats_export.h"
+    DESTINATION
+      include
+    COMPONENT
+      Devel
+  )
+
+  include(CMakePackageConfigHelpers)
+  write_basic_package_version_file(
+    "${CMAKE_CURRENT_BINARY_DIR}/ClimbingStatsConfigVersion.cmake"
+    VERSION ${Upstream_VERSION}
+    COMPATIBILITY AnyNewerVersion
+  )
+
+  set(ConfigPackageLocation lib/cmake/ClimbingStats)
+  install(EXPORT ClimbingStatsTargets
+    FILE
+      ClimbingStatsTargets.cmake
+    NAMESPACE
+      Upstream::
+    DESTINATION
+      ${ConfigPackageLocation}
+  )
+  install(
+    FILES
+      cmake/ClimbingStatsConfig.cmake
+      "${CMAKE_CURRENT_BINARY_DIR}/ClimbingStatsConfigVersion.cmake"
+    DESTINATION
+      ${ConfigPackageLocation}
+    COMPONENT
+      Devel
+  )
+
+The :module:`CMakePackageConfigHelpers` module provides a macro for creating
+a simple ``ConfigVersion.cmake`` file.  This file sets the version of the
+package.  It is read by CMake when :command:`find_package` is called to
+determine the compatibility with the requested version, and to set some
+version-specific variables ``<Package>_VERSION``, ``<Package>_VERSION_MAJOR``,
+``<Package>_VERSION_MINOR`` etc.  The :command:`install(EXPORT)` command is
+used to export the targets in the ``ClimbingStatsTargets`` export-set, defined
+previously by the :command:`install(TARGETS)` command. This command generates
+the ``ClimbingStatsTargets.cmake`` file to contain :prop_tgt:`IMPORTED`
+targets, suitable for use by downsteams and arranges to install it to
+``lib/cmake/ClimbingStats``.  The generated ``ClimbingStatsConfigVersion.cmake``
+and a ``cmake/ClimbingStatsConfig.cmake`` are installed to the same location,
+completing the package.
+
+A ``NAMESPACE`` with double-colons is specified when exporting the targets
+for installation.  This convention of double-colons gives CMake a hint that
+the name is an :prop_tgt:`IMPORTED` target when it is used by downstreams
+with the :command:`target_link_libraries` command.  This way, CMake can
+issue a diagnostic if the package providing it has not yet been found.
+
+In this case, when using :command:`install(TARGETS)` the ``INCLUDES DESTINATION``
+was specified.  This causes the ``IMPORTED`` targets to have their
+:prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES` populated with the ``include``
+directory in the :variable:`CMAKE_INSTALL_PREFIX`.  When the ``IMPORTED``
+target is used by downsteam, it automatically consumes the entries from
+that property.
+
+In this case, the ``ClimbingStatsConfig.cmake`` file could be as simple as:
+
+.. code-block:: cmake
+
+  include("${CMAKE_CURRENT_LIST_DIR}/ClimbingStatsTargets.cmake")
+
+As this allows downstreams to use the ``IMPORTED`` targets.  If any macros
+should be provided by the ``ClimbingStats`` package, they should
+be in a separate file which is installed to the same location as the
+``ClimbingStatsConfig.cmake`` file, and included from there.
+
+This can also be extended to cover dependencies:
+
+.. code-block:: cmake
+
+  # ...
+  add_library(ClimbingStats SHARED climbingstats.cpp)
+  generate_export_header(ClimbingStats)
+
+  find_package(Stats 2.6.4 REQUIRED)
+  target_link_libraries(ClimbingStats PUBLIC Stats::Types)
+
+As the ``Stats::Types`` target is a ``PUBLIC`` dependency of ``ClimbingStats``,
+downsteams must also find the ``Stats`` package and link to the ``Stats::Types``
+library.  The ``Stats`` package should be found in the ``ClimbingStatsConfig.cmake``
+file to ensure this.  The ``find_dependency`` macro from the
+:module:`CMakeFindDependencyMacro` helps with this by propagating
+whether the package is ``REQUIRED``, or ``QUIET`` etc.  All ``REQUIRED``
+dependencies of a package should be found in the ``Config.cmake`` file:
+
+.. code-block:: cmake
+
+  include(CMakePackageConfigHelpers)
+  find_dependency(Stats 2.6.4)
+
+  include("${CMAKE_CURRENT_LIST_DIR}/ClimbingStatsTargets.cmake")
+  include("${CMAKE_CURRENT_LIST_DIR}/ClimbingStatsMacros.cmake")
+
+The ``find_dependency`` macro also sets ``ClimbingStats_FOUND`` to ``False`` if
+the dependency is not found, along with a diagnostic that the ``ClimbingStats``
+package can not be used without the ``Stats`` package.
+
+If ``COMPONENTS`` are specified when the downstream uses :command:`find_package`,
+they are listed in the ``<Package>_FIND_COMPONENTS`` variable. If a particular
+component is non-optional, then the ``<Package>_FIND_REQUIRED_<comp>`` will
+be true. This can be tested with logic in the package configuration file:
+
+.. code-block:: cmake
+
+  include(CMakePackageConfigHelpers)
+  find_dependency(Stats 2.6.4)
+
+  include("${CMAKE_CURRENT_LIST_DIR}/ClimbingStatsTargets.cmake")
+  include("${CMAKE_CURRENT_LIST_DIR}/ClimbingStatsMacros.cmake")
+
+  set(_supported_components Plot Table)
+
+  foreach(_comp ${ClimbingStats_FIND_COMPONENTS})
+    if (NOT ";${_supported_components};" MATCHES _comp)
+      set(ClimbingStats_FOUND False)
+      set(ClimbingStats_NOTFOUND_MESSAGE "Specified unsupported component: ${_comp}")
+    endif()
+    include("${CMAKE_CURRENT_LIST_DIR}ClimbingStats${_comp}Targets.cmake")
+  endforeach()
+
+Here, the ``ClimbingStats_NOTFOUND_MESSAGE`` is set to a diagnosis that the package
+could not be found because an invalid component was specified.  This message
+variable can be set for any case where the ``_FOUND`` variable is set to ``False``,
+and will be displayed to the user.

+ 1 - 0
Help/module/CMakeFindDependencyMacro.rst

@@ -0,0 +1 @@
+.. cmake-module:: ../../Modules/CMakeFindDependencyMacro.cmake

+ 58 - 0
Modules/CMakeFindDependencyMacro.cmake

@@ -0,0 +1,58 @@
+#.rst:
+# CMakeFindDependencyMacro
+# -------------------------
+#
+# ::
+#
+#     find_dependency(<dep> [<version>])
+#
+#
+# ``find_dependency()`` wraps a :command:`find_package` call for a package
+# dependency. It is designed to be used in a <package>Config.cmake file, and it
+# forwards the correct parameters for EXACT, QUIET and REQUIRED which were
+# passed to the original :command:`find_package` call.  It also sets an
+# informative diagnostic message if the dependency could not be found.
+#
+
+#=============================================================================
+# Copyright 2013 Stephen Kelly <[email protected]>
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+#  License text for the above reference.)
+
+macro(find_dependency dep)
+  if (NOT ${dep}_FOUND)
+    if (${ARGV1})
+      set(version ${ARGV1})
+    endif()
+    set(exact_arg)
+    if(${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION_EXACT)
+      set(exact_arg EXACT)
+    endif()
+    set(quiet_arg)
+    if(${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY)
+      set(quiet_arg QUIET)
+    endif()
+    set(required_arg)
+    if(${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED)
+      set(required_arg REQUIRED)
+    endif()
+
+    find_package(${dep} ${version} ${exact_arg} ${quiet_arg} ${required_arg})
+    if (NOT ${dep}_FOUND)
+      set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "${CMAKE_FIND_PACKAGE_NAME} could not be found because dependency ${dep} could not be found.")
+      set(${CMAKE_FIND_PACKAGE_NAME}_FOUND False)
+      return()
+    endif()
+    set(required_arg)
+    set(quiet_arg)
+    set(exact_arg)
+  endif()
+endmacro()

+ 4 - 31
Modules/CMakePackageConfigHelpers.cmake

@@ -196,11 +196,13 @@
 include(CMakeParseArguments)
 
 include(WriteBasicConfigVersionFile)
+include(CMakeFindDependencyMacro)
 
 macro(WRITE_BASIC_PACKAGE_VERSION_FILE)
   write_basic_config_version_file(${ARGN})
 endmacro()
 
+set(cfpch_dir ${CMAKE_CURRENT_LIST_DIR})
 
 function(CONFIGURE_PACKAGE_CONFIG_FILE _inputFile _outputFile)
   set(options NO_SET_AND_CHECK_MACRO NO_CHECK_REQUIRED_COMPONENTS_MACRO NO_FIND_DEPENDENCY_MACRO)
@@ -290,37 +292,8 @@ endmacro()
   endif()
 
   if(NOT CCF_NO_FIND_DEPENDENCY_MACRO)
-    set(PACKAGE_INIT "${PACKAGE_INIT}
-macro(find_dependency dep)
-  if (NOT \${dep}_FOUND)
-    if (\${ARGV1})
-      set(version \${ARGV1})
-    endif()
-    set(exact_arg)
-    if(\${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION_EXACT)
-      set(exact_arg EXACT)
-    endif()
-    set(quiet_arg)
-    if(\${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY)
-      set(quiet_arg QUIET)
-    endif()
-    set(required_arg)
-    if(\${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED)
-      set(required_arg REQUIRED)
-    endif()
-
-    find_package(\${dep} \${version} \${exact_arg} \${quiet_arg} \${required_arg})
-    if (NOT \${dep}_FOUND)
-      set(\${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE \"\${CMAKE_FIND_PACKAGE_NAME} could not be found because dependency \${dep} could not be found.\")
-      set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND False)
-      return()
-    endif()
-    set(required_arg)
-    set(quiet_arg)
-    set(exact_arg)
-  endif()
-endmacro()
-")
+    file(READ "${cfpch_dir}/CMakeFindDependencyMacro.cmake" find_dependency_macro)
+    set(PACKAGE_INIT "${PACKAGE_INIT} ${find_dependency_macro}")
   endif()
 
   set(PACKAGE_INIT "${PACKAGE_INIT}