I wrote some documentation on how to write a find module for KDE's extra-cmake-modules project, and Stephen suggested upstreaming it. So attached is a patch that does that.
Note that this changes the recommendations from the Foo_LIBRARIES, Foo_INCLUDES and Foo_DEFINITIONS variables to creating imported targets (which are much easier and less error-prone to use). Alex
>From 2dc24347a65c1aeec6410dc265e6b0047b6a2317 Mon Sep 17 00:00:00 2001 From: Alex Merry <[email protected]> Date: Wed, 26 Mar 2014 18:54:02 +0000 Subject: [PATCH] Rewrite the find module developer documentation The recommendation now is to try to match what config files do, ie: create imported targets. This also walks through creating a sample find module. Modules that provide components are glossed over currently, despite being difficult to implement correctly. --- Help/manual/cmake-developer.7.rst | 408 +++++++++++++++++++++----------------- 1 file changed, 227 insertions(+), 181 deletions(-) diff --git a/Help/manual/cmake-developer.7.rst b/Help/manual/cmake-developer.7.rst index d025d63..0648cf8 100644 --- a/Help/manual/cmake-developer.7.rst +++ b/Help/manual/cmake-developer.7.rst @@ -664,124 +664,137 @@ For example, a ``Modules/Findxxx.cmake`` module may contain: <code> endmacro() -Find Modules ------------- - -A "find module" is a ``Modules/Find<package>.cmake`` file to be loaded -by the :command:`find_package` command when invoked for ``<package>``. - -We would like all ``FindXxx.cmake`` files to produce consistent variable -names. Please use the following consistent variable names for general use. - -Xxx_INCLUDE_DIRS - The final set of include directories listed in one variable for use by client - code. This should not be a cache entry. - -Xxx_LIBRARIES - The libraries to link against to use Xxx. These should include full paths. - This should not be a cache entry. - -Xxx_DEFINITIONS - Definitions to use when compiling code that uses Xxx. This really shouldn't - include options such as (-DHAS_JPEG)that a client source-code file uses to - decide whether to #include <jpeg.h> - -Xxx_EXECUTABLE - Where to find the Xxx tool. - -Xxx_Yyy_EXECUTABLE - Where to find the Yyy tool that comes with Xxx. - -Xxx_LIBRARY_DIRS - Optionally, the final set of library directories listed in one variable for - use by client code. This should not be a cache entry. +After the top documentation block, leave a *BLANK* line, and then add a +copyright and licence notice block like this one (change only the year +range and name):: -Xxx_ROOT_DIR - Where to find the base directory of Xxx. - -Xxx_VERSION_Yy - Expect Version Yy if true. Make sure at most one of these is ever true. - -Xxx_WRAP_Yy - If False, do not try to use the relevant CMake wrapping command. - -Xxx_Yy_FOUND - If False, optional Yy part of Xxx sytem is not available. - -Xxx_FOUND - Set to false, or undefined, if we haven't found, or don't want to use Xxx. - -Xxx_NOT_FOUND_MESSAGE - Should be set by config-files in the case that it has set Xxx_FOUND to FALSE. - The contained message will be printed by the find_package() command and by - find_package_handle_standard_args() to inform the user about the problem. - -Xxx_RUNTIME_LIBRARY_DIRS - Optionally, the runtime library search path for use when running an - executable linked to shared libraries. The list should be used by user code - to create the PATH on windows or LD_LIBRARY_PATH on unix. This should not be - a cache entry. - -Xxx_VERSION_STRING - A human-readable string containing the version of the package found, if any. + #============================================================================= + # Copyright 2009-2011 Your Name + # + # 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.) -Xxx_VERSION_MAJOR - The major version of the package found, if any. +Test the documentation formatting by running +``cmake --help-module <module-name>``, and also by enabling the +``SPHINX_HTML`` and ``SPHINX_MAN`` options to build the documentation. +Edit the comments until generated documentation looks satisfactory. To +have a .cmake file in this directory NOT show up in the modules +documentation, simply leave out the ``Help/module/<module-name>.rst`` +file and the ``Help/manual/cmake-modules.7.rst`` toctree entry. -Xxx_VERSION_MINOR - The minor version of the package found, if any. -Xxx_VERSION_PATCH - The patch version of the package found, if any. +Find Modules +------------ -You do not have to provide all of the above variables. You should provide -Xxx_FOUND under most circumstances. If Xxx is a library, then Xxx_LIBRARIES, -should also be defined, and Xxx_INCLUDE_DIRS should usually be defined (I -guess libm.a might be an exception) +A "find module" is a ``Modules/Find<package>.cmake`` file to be loaded +by the :command:`find_package` command when invoked for ``<package>``. -The following names should not usually be used in CMakeLists.txt files, but -they may be usefully modified in users' CMake Caches to control stuff. +The primary task of a find module is to determine whether a package +exists on the system, set the ``<package>_FOUND`` variable to reflect +this and provide any variables, macros and imported targets required to +use the package. + +Find modules should aim to behave as much like ``<package>Config.cmake`` +files as possible. This means they should provide imported targets +(rather than the traditional ``<package>_LIBRARIES`` and similar +variables). However, if a find module of the same name previously +existed, the old variables should be provided for backwards +compatibility. These should be documented as deprecated. + +A FindFoo.cmake module will typically be loaded by the command:: + + find_package(Foo [major[.minor[.patch[.tweak]]]] + [EXACT] [QUIET] [REQUIRED] + [[COMPONENTS] [components...]] + [OPTIONAL_COMPONENTS components...] + [NO_POLICY_SCOPE]) + +See the :command:`find_package` documentation for details on what +variables are set for the find module. Most of these are dealt with by +using :module:`FindPackageHandleStandardArgs`. + +Briefly, the module should only locate versions of the package +compatible with the requested version, as described by the +``Foo_FIND_VERSION`` family of variables. If ``Foo_FIND_QUIETLY`` is +set to true, it should avoid printing messages, including anything +complaining about the package not being found. If ``Foo_FIND_REQUIRED`` +is set to true, the module should issue a ``FATAL_ERROR`` if the package +cannot be found. If neither are set to true, it should print a +non-fatal message if it cannot find the package. + +Packages that find multiple semi-independent parts (like bundles of +libraries) should search for the components listed in +``Foo_FIND_COMPONENTS`` if it is set , and only set ``Foo_FOUND`` to +true if for each searched-for component ``<c>`` that was not found, +``Foo_FIND_REQUIRED_<c>`` is not set to true. The ``HANDLE_COMPONENTS`` +argument of ``find_package_handle_standard_args()`` can be used to +implement this. + +If ``Foo_FIND_COMPONENTS`` is not set, which modules are searched for +and required is up to the find module, but should be documented. + +For internal implementation, it is a generally accepted convention that +variables starting with underscore are for temporary use only. + +Like all modules, find modules should be properly documented. To add a +module to the CMake documentation, follow the steps in the `Module +Documentation`_ section above. + +A Sample Find Module +-------------------- -Xxx_LIBRARY - Name of Xxx Library. A User may set this and Xxx_INCLUDE_DIR to ignore to - force non-use of Xxx. +We will describe how to create a simple find module for a library +``Foo``. -Xxx_Yy_LIBRARY - Name of Yy library that is part of the Xxx system. It may or may not be - required to use Xxx. +The first thing that is needed is documentation. CMake's documentation +system requires you to start the file with a documentation marker and +the name of the module. You should follow this with a simple statement +of what the module does: :: -Xxx_INCLUDE_DIR - Where to find xxx.h, etc. (Xxx_INCLUDE_PATH was considered bad because a path - includes an actual filename.) + #.rst: + # FindFoo + # ------- + # + # Finds the Foo library + # -Xxx_Yy_INCLUDE_DIR - Where to find xxx_yy.h, etc. +More description may be required for some packages. If there are +caveats or other details users of the module should be aware of, you can +add further paragraphs below this. Then you need to document what +variables and imported targets are set by the module, such as :: -For tidiness's sake, try to keep as many options as possible out of the cache, -leaving at least one option which can be used to disable use of the module, or -locate a not-found library (e.g. Xxx_ROOT_DIR). For the same reason, mark -most cache options as advanced. + # This will define the following variables: :: + # + # Foo_FOUND - True if the system has the Foo library + # Foo_VERSION - The version of the Foo library which was found + # + # and the following imported targets: :: + # + # Foo::Foo - The Foo library -If you need other commands to do special things then it should still begin -with ``Xxx_``. This gives a sort of namespace effect and keeps things tidy for the -user. You should put comments describing all the exported settings, plus -descriptions of any the users can use to control stuff. +If you need to provide backwards compatibility, document that here: :: -You really should also provide backwards compatibility any old settings that -were actually in use. Make sure you comment them as deprecated, so that -no-one starts using them. + # The following compatibility variables will also be defined, although + # the imported targets should be used instead: :: + # + # Foo_LIBRARIES - Link to these to use the Foo library + # Foo_INCLUDES_DIRS - Include directory for the Foo library + # Foo_DEFINITIONS - Compiler flags required to link against the Foo library + # Foo_VERSION_STRING - The version of the Foo library which was found -To add a module to the CMake documentation, follow the steps in the -`Module Documentation`_ section above. Test the documentation formatting -by running ``cmake --help-module FindXxx``, and also by enabling the -``SPHINX_HTML`` and ``SPHINX_MAN`` options to build the documentation. -Edit the comments until generated documentation looks satisfactory. -To have a .cmake file in this directory NOT show up in the modules -documentation, simply leave out the ``Help/module/<module-name>.rst`` file -and the ``Help/manual/cmake-modules.7.rst`` toctree entry. +If the package provides any macros, they should be listed here, but can +be documented where they are defined. See the `Module +Documentation`_ section above for more details. -After the documentation, leave a *BLANK* line, and then add a -copyright and licence notice block like this one:: +After the documentation, leave a blank line, and then add a copyright and +licence notice block: :: #============================================================================= # Copyright 2009-2011 Your Name @@ -796,81 +809,114 @@ copyright and licence notice block like this one:: # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) -The layout of the notice block is strictly enforced by the ``ModuleNotices`` -test. Only the year range and name may be changed freely. - -A FindXxx.cmake module will typically be loaded by the command:: - - FIND_PACKAGE(Xxx [major[.minor[.patch[.tweak]]]] [EXACT] - [QUIET] [[REQUIRED|COMPONENTS] [components...]]) - -If any version numbers are given to the command it will set the following -variables before loading the module: - -Xxx_FIND_VERSION - full requested version string - -Xxx_FIND_VERSION_MAJOR - major version if requested, else 0 - -Xxx_FIND_VERSION_MINOR - minor version if requested, else 0 - -Xxx_FIND_VERSION_PATCH - patch version if requested, else 0 - -Xxx_FIND_VERSION_TWEAK - tweak version if requested, else 0 - -Xxx_FIND_VERSION_COUNT - number of version components, 0 to 4 - -Xxx_FIND_VERSION_EXACT - true if EXACT option was given - -If the find module supports versioning it should locate a version of -the package that is compatible with the version requested. If a -compatible version of the package cannot be found the module should -not report success. The version of the package found should be stored -in "Xxx_VERSION..." version variables documented by the module. - -If the QUIET option is given to the command it will set the variable -Xxx_FIND_QUIETLY to true before loading the FindXxx.cmake module. If -this variable is set the module should not complain about not being -able to find the package. If the -REQUIRED option is given to the command it will set the variable -Xxx_FIND_REQUIRED to true before loading the FindXxx.cmake module. If -this variable is set the module should issue a FATAL_ERROR if the -package cannot be found. -If neither the QUIET nor REQUIRED options are given then the -FindXxx.cmake module should look for the package and complain without -error if the module is not found. - -FIND_PACKAGE() will set the variable CMAKE_FIND_PACKAGE_NAME to -contain the actual name of the package. - -A package can provide sub-components. -Those components can be listed after the COMPONENTS (or REQUIRED) or -OPTIONAL_COMPONENTS keywords. The set of all listed components will be -specified in a Xxx_FIND_COMPONENTS variable. -For each package-specific component, say Yyy, a variable Xxx_FIND_REQUIRED_Yyy -will be set to true if it listed after COMPONENTS and it will be set to false -if it was listed after OPTIONAL_COMPONENTS. -Using those variables a FindXxx.cmake module and also a XxxConfig.cmake -package configuration file can determine whether and which components have -been requested, and whether they were requested as required or as optional. -For each of the requested components a Xxx_Yyy_FOUND variable should be set -accordingly. -The per-package Xxx_FOUND variable should be only set to true if all requested -required components have been found. A missing optional component should not -keep the Xxx_FOUND variable from being set to true. -If the package provides Xxx_INCLUDE_DIRS and Xxx_LIBRARIES variables, the -include dirs and libraries for all components which were requested and which -have been found should be added to those two variables. - -To get this behavior you can use the FIND_PACKAGE_HANDLE_STANDARD_ARGS() -macro, as an example see FindJPEG.cmake. - -For internal implementation, it's a generally accepted convention that -variables starting with underscore are for temporary use only. (variable -starting with an underscore are not intended as a reserved prefix). +If the module is new to CMake, you may want to provide a warning for +projects that do not require a high enough CMake version: :: + + if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 3.0.0) + message(AUTHOR_WARNING "Your project should require at least CMake 3.0.0 to use FindFoo.cmake") + endif() + +Now the actual libraries and so on have to be found. The code here will +obviously vary from module to module (dealing with that, after all, is the +point of find modules), but there tends to be a common pattern for libraries. + +First, we try to use ``pkg-config`` to find the library. Note that we +cannot rely on this, as it may not be available, but it provides a good +starting point. :: + + find_package(PkgConfig) + pkg_check_modules(PC_Foo QUIET Foo) + +This should define some variables starting ``PC_Foo_`` that contain the +information from the ``Foo.pc`` file. + +Now we need to find the libraries and include files; we use the +information from ``pkg-config`` to provide hints to CMake about where to +look: :: + + find_path(Foo_INCLUDE_DIR + NAMES foo.h + PATHS ${PC_Foo_INCLUDE_DIRS} + # if you need to put #include <Foo/foo.h> in your code, add: + PATH_SUFFIXES Foo + ) + find_library(Foo_LIBRARY + NAMES foo + PATHS ${PC_Foo_LIBRARY_DIRS} + ) + +If you have a good way of getting the version (from a header file, for +example), you can use that information to set ``Foo_VERSION``. +Otherwise, attempt to use the information from ``pkg-config``: :: + + set(Foo_VERSION ${PC_Foo_VERSION}) + +Now we can use :module:`FindPackageHandleStandardArgs` to do most of the +rest of the work for us. :: + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Foo + FOUND_VAR Foo_FOUND + REQUIRED_VARS + Foo_LIBRARY + Foo_INCLUDE_DIR + VERSION_VAR Foo_VERSION + ) + +This will check that the ``REQUIRED_VARS`` contain values (that do not +end in ``-NOTFOUND``) and set ``Foo_FOUND`` appropriately. It will also +cache those values. If ``Foo_VERSION`` is set, and a required version +was passed to :command:`find_package`, it will check the requested version +against the one in ``Foo_VERSION``. It will also print messages as +appropriate; note that if the package was found, it will print the +contents of the first required variable to indicate where it was found. + +We add an imported target for the library. Note that we do this after +calling ``find_package_handle_standard_args()`` so that we can use the +``Foo_FOUND`` variable. Imported targets should be namespaced (hence +the ``Foo::`` prefix); CMake will recognize that values passed to +:command:`target_link_libraries` that contain ``::`` in their name are +supposed to be imported targets (rather than just library names), and +will produce appropriate diagnostic messages if that target does not +exist (see policy :policy:`CMP0028`). :: + + if(Foo_FOUND AND NOT TARGET Foo::Foo) + add_library(Foo::Foo UNKNOWN IMPORTED) + set_target_properties(Foo::Foo PROPERTIES + IMPORTED_LOCATION "${Foo_LIBRARY}" + INTERFACE_COMPILE_OPTIONS "${PC_Foo_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Foo_INCLUDE_DIR}" + ) + endif() + +One thing to note about this is that the ``INTERFACE_INCLUDE_DIRECTORIES`` and +similar properties should only contain information about the target itself, and +not any of its dependencies. Instead, those dependencies should also be +targets, and CMake should be told that they are dependencies of this target. +CMake will then combine all the necessary information automatically. + +We should also provide some information about the package, such as where to +download it. :: + + include(FeatureSummary) + set_package_properties(Foo PROPERTIES + URL "http://www.foo.example.com/" + DESCRIPTION "A library for doing useful things" + ) + +Most of the cache variables should be hidden in the ``ccmake`` interface unless +the user explicitly asks to edit them: :: + + mark_as_advanced( + Foo_INCLUDE_DIR + Foo_LIBRARY + ) + +If this module replaces an older version, you should set compatibility variables +to cause the least disruption possible. :: + + # compatibility variables + set(Foo_LIBRARIES ${Foo_LIBRARY}) + set(Foo_DEFINITIONS ${PC_Foo_CFLAGS_OTHER}) + set(Foo_INCLUDE_DIRS ${Foo_INCLUDE_DIR}) + set(Foo_VERSION_STRING ${Foo_VERSION}) -- 1.9.1
-- Powered by www.kitware.com Please keep messages on-topic and check the CMake FAQ at: http://www.cmake.org/Wiki/CMake_FAQ Kitware offers various services to support the CMake community. For more information on each offering, please visit: CMake Support: http://cmake.org/cmake/help/support.html CMake Consulting: http://cmake.org/cmake/help/consulting.html CMake Training Courses: http://cmake.org/cmake/help/training.html Visit other Kitware open-source projects at http://www.kitware.com/opensource/opensource.html Follow this link to subscribe/unsubscribe: http://public.kitware.com/cgi-bin/mailman/listinfo/cmake-developers
