The open source OpenXR runtime
1# Copyright 2022, Collabora, Ltd.
2# Copyright 2000-2022, Kitware, Inc., Insight Software Consortium
3#
4# SPDX-License-Identifier: BSD-3-Clause
5#
6# CMake was initially developed by Kitware with the following sponsorship:
7# * National Library of Medicine at the National Institutes of Health
8# as part of the Insight Segmentation and Registration Toolkit (ITK).
9#
10# * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel
11# Visualization Initiative.
12#
13# * National Alliance for Medical Image Computing (NAMIC) is funded by the
14# National Institutes of Health through the NIH Roadmap for Medical Research,
15# Grant U54 EB005149.
16#
17# * Kitware, Inc.
18#
19# (Based on CMakeDependentOption)
20
21#[=======================================================================[.rst:
22OptionWithDeps
23--------------
24
25Macro to provide an option dependent on other options.
26
27This macro presents an option to the user only if a set of other
28conditions are true. If it is already specified by the user but the
29conditions are not true, it triggers an error.
30
31This is based on cmake_dependent_options but meets common expectations better:
32if you explicitly try to enable something that is not available, you get an error
33instead of having it silently disabled.
34
35.. command:: option_with_deps
36
37 .. code-block:: cmake
38
39 option_with_deps(<option> "<help_text>" [DEFAULT <default>] DEPENDS [<depends>...])
40
41 Describes a build option that has dependencies. If the option is requested,
42 but the depends are not satisfied, an error is issued. DEPENDS is a list of
43 conditions to check: all must be true to make the option available.
44 Otherwise, a local variable named ``<option>`` is set to ``OFF``.
45
46 When ``<option>`` is available, the given ``<help_text>`` and initial
47 ``<default>`` are used. Otherwise, any value set by the user is preserved for
48 when ``<depends>`` is satisfied in the future.
49
50 Note that the ``<option>`` variable only has a value which satisfies the
51 ``<depends>`` condition within the scope of the caller because it is a local
52 variable.
53
54 Elements of ``<depends>`` cannot contain parentheses nor "AND" (each item is
55 implicitly "ANDed" together). Be sure to quote OR and NOT expressions, and avoid
56 complex expressions (such as with escaped quotes, etc) since they may fail,
57 especially before CMake 3.18.
58
59Example invocation:
60
61.. code-block:: cmake
62
63 option_with_deps(USE_PACKAGE_ABC "Use Abc" DEPENDS "USE_PACKAGE_XYZ" "NOT USE_CONFLICTING_PACKAGE")
64
65If ``USE_PACKAGE_XYZ`` is true and ``USE_CONFLICTING_PACKAGE`` is false, this provides
66an option called ``USE_PACKAGE_ABC`` that defaults to ON. Otherwise, it sets
67``USE_PACKAGE_ABC`` to OFF and hides the option from the user. If the status of
68``USE_PACKAGE_XYZ`` or ``USE_CONFLICTING_PACKAGE`` ever changes, any value for the
69``USE_PACKAGE_ABC`` option is saved so that when the option is re-enabled it retains
70its old value.
71
72#]=======================================================================]
73
74function(option_with_deps option doc)
75 set(options)
76 set(oneValueArgs DEFAULT)
77 set(multiValueArgs DEPENDS)
78 cmake_parse_arguments(_option_deps "${options}" "${oneValueArgs}"
79 "${multiValueArgs}" ${ARGN})
80
81 if(NOT DEFINED _option_deps_DEFAULT)
82 set(_option_deps_DEFAULT ON)
83 endif()
84
85 # Check for invalid/bad depends args
86 foreach(d ${_option_deps_DEPENDS})
87 if("${d}" MATCHES "[(]")
88 message(
89 FATAL_ERROR "option_with_deps does not support parens in deps")
90 endif()
91 if("${d}" MATCHES "\\bAND\\b")
92 message(
93 FATAL_ERROR
94 "option_with_deps treats every deps item as being implicitly 'ANDed' together"
95 )
96 endif()
97 if("${d}" STREQUAL "OR")
98 message(FATAL_ERROR "option_with_deps needs OR expressions quoted")
99 endif()
100 if("${d}" STREQUAL "NOT")
101 message(FATAL_ERROR "option_with_deps needs NOT expressions quoted")
102 endif()
103 endforeach()
104
105 # This is a case we removed from the original CMakeDependentOption module
106 if(NOT (${option}_ISSET MATCHES "^${option}_ISSET$"))
107 message(
108 FATAL_ERROR
109 "Probably too old of CMake version to cope with this module")
110 endif()
111
112 # Check the actual deps, determine if the option is available
113 set(_avail ON)
114 foreach(d ${_option_deps_DEPENDS})
115 cmake_language(
116 EVAL
117 CODE
118 "
119 if(${d})
120 else()
121 set(_avail OFF)
122 set(_cond ${d})
123 endif()")
124 endforeach()
125
126 # Error if option was requested but not available
127 if("${${option}}" AND NOT "${_avail}")
128 message(
129 FATAL_ERROR
130 "${option} specified but not available: failed check ${_cond}")
131 endif()
132
133 # Handle remaining cases
134 set(_already_defined OFF)
135 if(DEFINED ${option})
136 set(_already_defined ON)
137 endif()
138 if(${_avail})
139 # Set a cache variable: the value here will not override an already-set value.
140 option(${option} "${doc}" "${_option_deps_DEFAULT}")
141
142 if(NOT _already_defined)
143 # Needed to force this for some reason
144 set(${option}
145 "${${option}}"
146 CACHE BOOL "${doc}" FORCE)
147 endif()
148 else()
149 # Don't set a cache variable for something that's not available
150 set(${option}
151 OFF
152 PARENT_SCOPE)
153 endif()
154endfunction()