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

Reply via email to