1.. ecm-manual-description: ECM Developer Reference
2
3ecm-developer(7)
4****************
5
6.. only:: html or latex
7
8   .. contents::
9
10
11Writing Modules
12===============
13
14The CMake 3 documentation (and `cmake-developer(7)`_ in particular) has a lot of
15useful information about writing CMake modules, including a large section
16devoted to find modules. This guide will only highlight things that are
17particular to the Extra CMake Modules project.
18
19Most of these are stylistic points. For example, the license header for a module
20in ECM should look like:
21
22.. code-block:: cmake
23
24  # SPDX-FileCopyrightText: 20XX Your Name <your.email@example.com>
25  #
26  # SPDX-License-Identifier: BSD-3-Clause
27
28Documentation is written in reStructuredText format and put inside a bracket
29comment with a ``.rst:`` id after the opening bracket:
30
31.. code-block:: cmake
32
33  #[=======================================================================[.rst:
34  The docs
35  #]=======================================================================]
36
37(docs/sphinx/ext/ecm.py has code to extract the rst text from a comment with
38such wrapping)
39
40Functions should be used instead of macros unless there is a good reason not to
41(and that reason should be noted in a comment), and lowercase should be used for
42macros, functions and commands.
43
444 spaces is the generally-recommended indent, although there are several files
45that use 2 spaces; consistency within a file is more important than consistency
46across files.
47
48If in doubt, look at how other modules in Extra CMake Modules are written, and
49follow the same pattern.
50
51
52Find Modules
53------------
54
55A good template for find module documentation is:
56
57.. code-block:: cmake
58
59  #[=======================================================================[.rst:
60  FindFoo
61  -------
62
63  Finds the Foo library.
64
65  This will define the following variables:
66
67  ``Foo_FOUND``
68      True if (the requested version of) Foo is available
69  ``Foo_VERSION``
70      The version of Foo, if it is found
71  ``Foo_LIBRARIES``
72      This can be passed to target_link_libraries() instead of the ``Foo::Foo``
73      target
74  ``Foo_INCLUDE_DIRS``
75      This should be passed to target_include_directories() if the target is not
76      used for linking
77  ``Foo_DEFINITIONS``
78      This should be passed to target_compile_options() if the target is not
79      used for linking
80
81  If ``Foo_FOUND`` is TRUE, it will also define the following imported target:
82
83  ``Foo::Foo``
84      The Foo library
85
86  In general we recommend using the imported target, as it is easier to use.
87  Bear in mind, however, that if the target is in the link interface of an
88  exported library, it must be made available by the package config file.
89  #]=======================================================================]
90
91Note the use of definition lists for the variables.
92
93Because of the :module:`ECMUseFindModules` module, projects may easily make
94local copies of find modules, and may install those copies with their own CMake
95project config files. For this reason, find modules should include the full BSD
963-clause license::
97
98  #=============================================================================
99  # Copyright 20XX Your Name <your.email@example.com>
100  #
101  # Redistribution and use in source and binary forms, with or without
102  # modification, are permitted provided that the following conditions
103  # are met:
104  #
105  # 1. Redistributions of source code must retain the copyright
106  #    notice, this list of conditions and the following disclaimer.
107  # 2. Redistributions in binary form must reproduce the copyright
108  #    notice, this list of conditions and the following disclaimer in the
109  #    documentation and/or other materials provided with the distribution.
110  # 3. The name of the author may not be used to endorse or promote products
111  #    derived from this software without specific prior written permission.
112  #
113  # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
114  # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
115  # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
116  # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
117  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
118  # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
119  # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
120  # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
121  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
122  # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
123  #=============================================================================
124
125Find modules should always provide imported targets in addition to the
126traditional variables (like ``Foo_LIBRARIES``, etc).
127
128Unlike find modules shipped with CMake, if the module requires a specific CMake
129version it is not enough to warn when the minimum required version is not high
130enough: you should also produce an error when the actual CMake version being
131used is not high enough. This can be done with:
132
133.. code-block:: cmake
134
135  if(CMAKE_VERSION VERSION_LESS 3.16.0)
136      message(FATAL_ERROR "CMake 3.16.0 is required by FindFoo.cmake")
137  endif()
138  if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 3.16.0)
139      message(AUTHOR_WARNING "Your project should require at least CMake 3.16.0 to use FindFoo.cmake")
140  endif()
141
142The :module:`ECMFindModuleHelpers` module has several useful functions and
143macros. For example, it allows you to replace the above version check with:
144
145.. code-block:: cmake
146
147  ecm_find_package_version_check(Foo)
148
149Components
150~~~~~~~~~~
151
152Using :module:`ECMFindModuleHelpers`, creating a find module for a library with
153several inter-dependent components is reasonably straightforward. After the
154documentation, you need to include the module and do the usual version check:
155
156.. code-block:: cmake
157
158  include(ECMFindModuleHelpers)
159  ecm_find_package_version_check(Foo)
160
161The important macros are ``ecm_find_package_parse_components`` and
162``ecm_find_package_handle_library_components``.  These take a list of
163components, and query other variables you provide to find out the information
164they require.  The documentation for :module:`ECMFindModuleHelpers` provides
165more information, but a simple setup might look like:
166
167.. code-block:: cmake
168
169  set(Foo_known_components Bar Baz)
170  set(Foo_Bar_pkg_config "foo-bar")
171  set(Foo_Bar_lib "bar")
172  set(Foo_Bar_header "foo/bar.h")
173  set(Foo_Bar_pkg_config "foo-baz")
174  set(Foo_Baz_lib "baz")
175  set(Foo_Baz_header "foo/baz.h")
176
177If ``Baz`` depends on ``Bar``, for example, you can specify this with
178
179.. code-block:: cmake
180
181  set(Foo_Baz_component_deps "Bar")
182
183Then call the macros:
184
185.. code-block:: cmake
186
187  ecm_find_package_parse_components(Foo
188      RESULT_VAR Foo_components
189      KNOWN_COMPONENTS ${Foo_known_components}
190  )
191  ecm_find_package_handle_library_components(Foo
192      COMPONENTS ${Foo_components}
193  )
194
195Of course, if your components need unusual handling, you may want to replace
196``ecm_find_package_handle_library_components`` with, for example, a ``foreach``
197loop over the components (the body of which should implement most of what a
198normal find module does, including setting ``Foo_<component>_FOUND``).
199
200At this point, you should set ``Foo_VERSION`` using whatever information you
201have available (such as from parsing header files).  Note that
202``ecm_find_package_handle_library_components`` will set it to the version
203reported by pkg-config of the first component found, but this depends on the
204presence of pkg-config files, and the version of a component may not be the same
205as the version of the whole package.  After that, finish off with
206
207.. code-block:: cmake
208
209  include(FindPackageHandleStandardArgs)
210  find_package_handle_standard_args(Foo
211      FOUND_VAR
212          Foo_FOUND
213      REQUIRED_VARS
214          Foo_LIBRARIES
215      VERSION_VAR
216          Foo_VERSION
217      HANDLE_COMPONENTS
218  )
219
220  include(FeatureSummary)
221  set_package_properties(Foo PROPERTIES
222      URL "https://www.foo.example.com/"
223      DESCRIPTION "A library for doing useful things")
224
225
226Submitting Modules
227==================
228
229Proposed new modules should be submitted using the `KDE Review Board instance`_,
230and be assigned to the ``buildsystem`` and ``extracmakemodules`` groups.  You
231should be able to point to two separate projects that will make use of the
232module.
233
234The mailing list can be found at
235https://mail.kde.org/mailman/listinfo/kde-buildsystem\ .
236
237
238.. _KDE Review Board instance: https://git.reviewboard.kde.org/
239.. _cmake-developer(7): https://www.cmake.org/cmake/help/git-master/manual/cmake-developer.7.html
240