1# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2# file Copyright.txt or https://cmake.org/licensing for details.
3
4#[=======================================================================[.rst:
5CMakePackageConfigHelpers
6-------------------------
7
8Helpers functions for creating config files that can be included by other
9projects to find and use a package.
10
11Adds the :command:`configure_package_config_file()` and
12:command:`write_basic_package_version_file()` commands.
13
14Generating a Package Configuration File
15^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16
17.. command:: configure_package_config_file
18
19 Create a config file for a project::
20
21   configure_package_config_file(<input> <output>
22     INSTALL_DESTINATION <path>
23     [PATH_VARS <var1> <var2> ... <varN>]
24     [NO_SET_AND_CHECK_MACRO]
25     [NO_CHECK_REQUIRED_COMPONENTS_MACRO]
26     [INSTALL_PREFIX <path>]
27     )
28
29``configure_package_config_file()`` should be used instead of the plain
30:command:`configure_file()` command when creating the ``<PackageName>Config.cmake``
31or ``<PackageName>-config.cmake`` file for installing a project or library.
32It helps making the resulting package relocatable by avoiding hardcoded paths
33in the installed ``Config.cmake`` file.
34
35In a ``FooConfig.cmake`` file there may be code like this to make the install
36destinations know to the using project:
37
38.. code-block:: cmake
39
40   set(FOO_INCLUDE_DIR   "@CMAKE_INSTALL_FULL_INCLUDEDIR@" )
41   set(FOO_DATA_DIR   "@CMAKE_INSTALL_PREFIX@/@RELATIVE_DATA_INSTALL_DIR@" )
42   set(FOO_ICONS_DIR   "@CMAKE_INSTALL_PREFIX@/share/icons" )
43   #...logic to determine installedPrefix from the own location...
44   set(FOO_CONFIG_DIR  "${installedPrefix}/@CONFIG_INSTALL_DIR@" )
45
46All 4 options shown above are not sufficient, since the first 3 hardcode the
47absolute directory locations, and the 4th case works only if the logic to
48determine the ``installedPrefix`` is correct, and if ``CONFIG_INSTALL_DIR``
49contains a relative path, which in general cannot be guaranteed.  This has the
50effect that the resulting ``FooConfig.cmake`` file would work poorly under
51Windows and OSX, where users are used to choose the install location of a
52binary package at install time, independent from how
53:variable:`CMAKE_INSTALL_PREFIX` was set at build/cmake time.
54
55Using ``configure_package_config_file`` helps.  If used correctly, it makes
56the resulting ``FooConfig.cmake`` file relocatable.  Usage:
57
581. write a ``FooConfig.cmake.in`` file as you are used to
592. insert a line containing only the string ``@PACKAGE_INIT@``
603. instead of ``set(FOO_DIR "@SOME_INSTALL_DIR@")``, use
61   ``set(FOO_DIR "@PACKAGE_SOME_INSTALL_DIR@")`` (this must be after the
62   ``@PACKAGE_INIT@`` line)
634. instead of using the normal :command:`configure_file()`, use
64   ``configure_package_config_file()``
65
66
67
68The ``<input>`` and ``<output>`` arguments are the input and output file, the
69same way as in :command:`configure_file()`.
70
71The ``<path>`` given to ``INSTALL_DESTINATION`` must be the destination where
72the ``FooConfig.cmake`` file will be installed to.  This path can either be
73absolute, or relative to the ``INSTALL_PREFIX`` path.
74
75The variables ``<var1>`` to ``<varN>`` given as ``PATH_VARS`` are the
76variables which contain install destinations.  For each of them the macro will
77create a helper variable ``PACKAGE_<var...>``.  These helper variables must be
78used in the ``FooConfig.cmake.in`` file for setting the installed location.
79They are calculated by ``configure_package_config_file`` so that they are
80always relative to the installed location of the package.  This works both for
81relative and also for absolute locations.  For absolute locations it works
82only if the absolute location is a subdirectory of ``INSTALL_PREFIX``.
83
84.. versionadded:: 3.1
85  If the ``INSTALL_PREFIX`` argument is passed, this is used as base path to
86  calculate all the relative paths.  The ``<path>`` argument must be an absolute
87  path.  If this argument is not passed, the :variable:`CMAKE_INSTALL_PREFIX`
88  variable will be used instead.  The default value is good when generating a
89  FooConfig.cmake file to use your package from the install tree.  When
90  generating a FooConfig.cmake file to use your package from the build tree this
91  option should be used.
92
93By default ``configure_package_config_file`` also generates two helper macros,
94``set_and_check()`` and ``check_required_components()`` into the
95``FooConfig.cmake`` file.
96
97``set_and_check()`` should be used instead of the normal ``set()`` command for
98setting directories and file locations.  Additionally to setting the variable
99it also checks that the referenced file or directory actually exists and fails
100with a ``FATAL_ERROR`` otherwise.  This makes sure that the created
101``FooConfig.cmake`` file does not contain wrong references.
102When using the ``NO_SET_AND_CHECK_MACRO``, this macro is not generated
103into the ``FooConfig.cmake`` file.
104
105``check_required_components(<PackageName>)`` should be called at the end of
106the ``FooConfig.cmake`` file. This macro checks whether all requested,
107non-optional components have been found, and if this is not the case, sets
108the ``Foo_FOUND`` variable to ``FALSE``, so that the package is considered to
109be not found.  It does that by testing the ``Foo_<Component>_FOUND``
110variables for all requested required components.  This macro should be
111called even if the package doesn't provide any components to make sure
112users are not specifying components erroneously.  When using the
113``NO_CHECK_REQUIRED_COMPONENTS_MACRO`` option, this macro is not generated
114into the ``FooConfig.cmake`` file.
115
116For an example see below the documentation for
117:command:`write_basic_package_version_file()`.
118
119Generating a Package Version File
120^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
121
122.. command:: write_basic_package_version_file
123
124 Create a version file for a project::
125
126   write_basic_package_version_file(<filename>
127     [VERSION <major.minor.patch>]
128     COMPATIBILITY <AnyNewerVersion|SameMajorVersion|SameMinorVersion|ExactVersion>
129     [ARCH_INDEPENDENT] )
130
131
132Writes a file for use as ``<PackageName>ConfigVersion.cmake`` file to
133``<filename>``.  See the documentation of :command:`find_package()` for
134details on this.
135
136``<filename>`` is the output filename, it should be in the build tree.
137``<major.minor.patch>`` is the version number of the project to be installed.
138
139If no ``VERSION`` is given, the :variable:`PROJECT_VERSION` variable is used.
140If this hasn't been set, it errors out.
141
142The ``COMPATIBILITY`` mode ``AnyNewerVersion`` means that the installed
143package version will be considered compatible if it is newer or exactly the
144same as the requested version.  This mode should be used for packages which
145are fully backward compatible, also across major versions.
146If ``SameMajorVersion`` is used instead, then the behavior differs from
147``AnyNewerVersion`` in that the major version number must be the same as
148requested, e.g.  version 2.0 will not be considered compatible if 1.0 is
149requested.  This mode should be used for packages which guarantee backward
150compatibility within the same major version.
151If ``SameMinorVersion`` is used, the behavior is the same as
152``SameMajorVersion``, but both major and minor version must be the same as
153requested, e.g version 0.2 will not be compatible if 0.1 is requested.
154If ``ExactVersion`` is used, then the package is only considered compatible if
155the requested version matches exactly its own version number (not considering
156the tweak version).  For example, version 1.2.3 of a package is only
157considered compatible to requested version 1.2.3.  This mode is for packages
158without compatibility guarantees.
159If your project has more elaborated version matching rules, you will need to
160write your own custom ``ConfigVersion.cmake`` file instead of using this
161macro.
162
163.. versionadded:: 3.11
164  The ``SameMinorVersion`` compatibility mode.
165
166.. versionadded:: 3.14
167  If ``ARCH_INDEPENDENT`` is given, the installed package version will be
168  considered compatible even if it was built for a different architecture than
169  the requested architecture.  Otherwise, an architecture check will be performed,
170  and the package will be considered compatible only if the architecture matches
171  exactly.  For example, if the package is built for a 32-bit architecture, the
172  package is only considered compatible if it is used on a 32-bit architecture,
173  unless ``ARCH_INDEPENDENT`` is given, in which case the package is considered
174  compatible on any architecture.
175
176.. note:: ``ARCH_INDEPENDENT`` is intended for header-only libraries or similar
177  packages with no binaries.
178
179.. versionadded:: 3.19
180  ``COMPATIBILITY_MODE`` ``AnyNewerVersion``, ``SameMajorVersion`` and
181  ``SameMinorVersion`` handle the version range if any is specified
182  (see :command:`find_package` command for the details).
183  ``ExactVersion`` mode is incompatible with version ranges and will display an
184  author warning if one is specified.
185
186Internally, this macro executes :command:`configure_file()` to create the
187resulting version file.  Depending on the ``COMPATIBILITY``, the corresponding
188``BasicConfigVersion-<COMPATIBILITY>.cmake.in`` file is used.
189Please note that these files are internal to CMake and you should not call
190:command:`configure_file()` on them yourself, but they can be used as starting
191point to create more sophisticted custom ``ConfigVersion.cmake`` files.
192
193Example Generating Package Files
194^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
195
196Example using both :command:`configure_package_config_file` and
197``write_basic_package_version_file()``:
198
199``CMakeLists.txt``:
200
201.. code-block:: cmake
202
203   set(INCLUDE_INSTALL_DIR include/ ... CACHE )
204   set(LIB_INSTALL_DIR lib/ ... CACHE )
205   set(SYSCONFIG_INSTALL_DIR etc/foo/ ... CACHE )
206   #...
207   include(CMakePackageConfigHelpers)
208   configure_package_config_file(FooConfig.cmake.in
209     ${CMAKE_CURRENT_BINARY_DIR}/FooConfig.cmake
210     INSTALL_DESTINATION ${LIB_INSTALL_DIR}/Foo/cmake
211     PATH_VARS INCLUDE_INSTALL_DIR SYSCONFIG_INSTALL_DIR)
212   write_basic_package_version_file(
213     ${CMAKE_CURRENT_BINARY_DIR}/FooConfigVersion.cmake
214     VERSION 1.2.3
215     COMPATIBILITY SameMajorVersion )
216   install(FILES ${CMAKE_CURRENT_BINARY_DIR}/FooConfig.cmake
217                 ${CMAKE_CURRENT_BINARY_DIR}/FooConfigVersion.cmake
218           DESTINATION ${LIB_INSTALL_DIR}/Foo/cmake )
219
220``FooConfig.cmake.in``:
221
222::
223
224   set(FOO_VERSION x.y.z)
225   ...
226   @PACKAGE_INIT@
227   ...
228   set_and_check(FOO_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
229   set_and_check(FOO_SYSCONFIG_DIR "@PACKAGE_SYSCONFIG_INSTALL_DIR@")
230
231   check_required_components(Foo)
232#]=======================================================================]
233
234include(WriteBasicConfigVersionFile)
235
236macro(WRITE_BASIC_PACKAGE_VERSION_FILE)
237  write_basic_config_version_file(${ARGN})
238endmacro()
239
240function(CONFIGURE_PACKAGE_CONFIG_FILE _inputFile _outputFile)
241  set(options NO_SET_AND_CHECK_MACRO NO_CHECK_REQUIRED_COMPONENTS_MACRO)
242  set(oneValueArgs INSTALL_DESTINATION INSTALL_PREFIX)
243  set(multiValueArgs PATH_VARS )
244
245  cmake_parse_arguments(CCF "${options}" "${oneValueArgs}" "${multiValueArgs}"  ${ARGN})
246
247  if(CCF_UNPARSED_ARGUMENTS)
248    message(FATAL_ERROR "Unknown keywords given to CONFIGURE_PACKAGE_CONFIG_FILE(): \"${CCF_UNPARSED_ARGUMENTS}\"")
249  endif()
250
251  if(NOT CCF_INSTALL_DESTINATION)
252    message(FATAL_ERROR "No INSTALL_DESTINATION given to CONFIGURE_PACKAGE_CONFIG_FILE()")
253  endif()
254
255  if(DEFINED CCF_INSTALL_PREFIX)
256    if(IS_ABSOLUTE "${CCF_INSTALL_PREFIX}")
257      set(installPrefix "${CCF_INSTALL_PREFIX}")
258    else()
259      message(FATAL_ERROR "INSTALL_PREFIX must be an absolute path")
260    endif()
261  elseif(IS_ABSOLUTE "${CMAKE_INSTALL_PREFIX}")
262    set(installPrefix "${CMAKE_INSTALL_PREFIX}")
263  else()
264    get_filename_component(installPrefix "${CMAKE_INSTALL_PREFIX}" ABSOLUTE)
265  endif()
266
267  if(IS_ABSOLUTE "${CCF_INSTALL_DESTINATION}")
268    set(absInstallDir "${CCF_INSTALL_DESTINATION}")
269  else()
270    set(absInstallDir "${installPrefix}/${CCF_INSTALL_DESTINATION}")
271  endif()
272
273  file(RELATIVE_PATH PACKAGE_RELATIVE_PATH "${absInstallDir}" "${installPrefix}" )
274
275  foreach(var ${CCF_PATH_VARS})
276    if(NOT DEFINED ${var})
277      message(FATAL_ERROR "Variable ${var} does not exist")
278    else()
279      if(IS_ABSOLUTE "${${var}}")
280        string(REPLACE "${installPrefix}" "\${PACKAGE_PREFIX_DIR}"
281                        PACKAGE_${var} "${${var}}")
282      else()
283        set(PACKAGE_${var} "\${PACKAGE_PREFIX_DIR}/${${var}}")
284      endif()
285    endif()
286  endforeach()
287
288  get_filename_component(inputFileName "${_inputFile}" NAME)
289
290  set(PACKAGE_INIT "
291####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() #######
292####### Any changes to this file will be overwritten by the next CMake run ####
293####### The input file was ${inputFileName}                            ########
294
295get_filename_component(PACKAGE_PREFIX_DIR \"\${CMAKE_CURRENT_LIST_DIR}/${PACKAGE_RELATIVE_PATH}\" ABSOLUTE)
296")
297
298  if("${absInstallDir}" MATCHES "^(/usr)?/lib(64)?/.+")
299    # Handle "/usr move" symlinks created by some Linux distros.
300    string(APPEND PACKAGE_INIT "
301# Use original install prefix when loaded through a \"/usr move\"
302# cross-prefix symbolic link such as /lib -> /usr/lib.
303get_filename_component(_realCurr \"\${CMAKE_CURRENT_LIST_DIR}\" REALPATH)
304get_filename_component(_realOrig \"${absInstallDir}\" REALPATH)
305if(_realCurr STREQUAL _realOrig)
306  set(PACKAGE_PREFIX_DIR \"${installPrefix}\")
307endif()
308unset(_realOrig)
309unset(_realCurr)
310")
311  endif()
312
313  if(NOT CCF_NO_SET_AND_CHECK_MACRO)
314    string(APPEND PACKAGE_INIT "
315macro(set_and_check _var _file)
316  set(\${_var} \"\${_file}\")
317  if(NOT EXISTS \"\${_file}\")
318    message(FATAL_ERROR \"File or directory \${_file} referenced by variable \${_var} does not exist !\")
319  endif()
320endmacro()
321")
322  endif()
323
324
325  if(NOT CCF_NO_CHECK_REQUIRED_COMPONENTS_MACRO)
326    string(APPEND PACKAGE_INIT "
327macro(check_required_components _NAME)
328  foreach(comp \${\${_NAME}_FIND_COMPONENTS})
329    if(NOT \${_NAME}_\${comp}_FOUND)
330      if(\${_NAME}_FIND_REQUIRED_\${comp})
331        set(\${_NAME}_FOUND FALSE)
332      endif()
333    endif()
334  endforeach()
335endmacro()
336")
337  endif()
338
339  string(APPEND PACKAGE_INIT "
340####################################################################################")
341
342  configure_file("${_inputFile}" "${_outputFile}" @ONLY)
343
344endfunction()
345