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