11#[=============================================================================[
22Set a CACHE variable that depends on a set of conditions.
33
4+ > [!WARNING]
5+ > TODO: This module is still under review to determine its usefulness.
6+ > Dependent variables may seem convenient for the application but may create
7+ > difficulties for anyone troubleshooting why a configuration isn't applied,
8+ > even though a configuration value has been set. In the end, build system
9+ > configuration isn't aiming to provide a HTML-form-alike functionality.
10+
411At the time of writing, there are 3 main ways in CMake to create non-internal
512cache variables that can be also customized from the outside using the `-D`
613command-line option, through CMake presets, or similar:
@@ -26,6 +33,7 @@ php_set(
2633 [CHOICES <string>...]
2734 [IF <condition> VALUE <value> [ELSE_VALUE <default>]] | [VALUE <value>]
2835 DOC <docstring>...
36+ [WARNING <warning>]
2937)
3038```
3139
@@ -60,50 +68,45 @@ It sets a CACHE `<variable>` of `<type>` to a `<value>`.
6068
6169* `DOC` is a short variable help text visible in the GUIs. Multiple strings are
6270 joined together.
71+
72+ * `WARNING` is optional text that is emitted when setting a variable from the
73+ command line or CMake presets but its condition is not met. Otherwise, a
74+ default warning is emitted.
6375#]=============================================================================]
6476
6577include_guard (GLOBAL )
6678
6779function (php_set)
68- # https://cmake.org/cmake/help/latest/policy/CMP0174.html
69- if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.31)
70- set (valueNew "VALUE" )
71- set (elseValueNew "ELSE_VALUE" )
72- else ()
73- set (valueOld "VALUE" )
74- set (elseValueOld "ELSE_VALUE" )
75- endif ()
76-
7780 cmake_parse_arguments (
7881 PARSE_ARGV
7982 1
80- parsed # prefix
81- "" # options
82- "${valueNew} ; TYPE;IF;${elseValueNew} " # one-value keywords
83- "${valueOld} ; CHOICES;DOC;${elseValueOld} " # multi-value keywords
83+ parsed # prefix
84+ "" # options
85+ "TYPE;IF;VALUE;ELSE_VALUE" # one-value keywords
86+ "CHOICES;DOC;WARNING" # multi-value keywords
8487 )
8588
86- if (parsed_UNPARSED_ARGUMENTS)
87- message (FATAL_ERROR "Bad arguments: ${parsed_UNPARSED_ARGUMENTS} " )
88- endif ()
89-
90- if (NOT DEFINED parsed_VALUE)
91- message (FATAL_ERROR "Missing VALUE argument" )
92- endif ()
93-
94- if (NOT parsed_TYPE)
95- message (FATAL_ERROR "Missing TYPE argument" )
96- elseif (NOT parsed_TYPE MATCHES "^(BOOL|FILEPATH|PATH|STRING)$" )
97- message (FATAL_ERROR "Unknown TYPE argument: ${parsed_TYPE} " )
98- endif ()
99-
100- if (NOT DEFINED parsed_IF AND DEFINED parsed_ELSE_VALUE)
101- message (FATAL_ERROR "Redundant ELSE_VALUE argument without IF condition" )
89+ # The cmake_parse_arguments() before 3.31 didn't define one-value keywords
90+ # with empty value of "". This fills the gap and behaves the same until it can
91+ # be removed. See: https://cmake.org/cmake/help/latest/policy/CMP0174.html
92+ if (CMAKE_VERSION VERSION_LESS 3.31)
93+ set (i 0)
94+ foreach (arg IN LISTS ARGN)
95+ math (EXPR i "${i} +1" )
96+ foreach (keyword VALUE ELSE_VALUE)
97+ if (
98+ arg STREQUAL "${keyword} "
99+ AND NOT DEFINED parsed_${keyword}
100+ AND DEFINED ARGV${i}
101+ AND "${ARGV${i} }" STREQUAL ""
102+ )
103+ set (parsed_${keyword} "" )
104+ endif ()
105+ endforeach ()
106+ endforeach ()
102107 endif ()
103108
104- if (NOT DEFINED parsed_DOC)
105- message (FATAL_ERROR "Missing DOC argument" )
106- endif ()
109+ _php_set_validate_arguments("${ARGN} " )
107110
108111 set (doc "" )
109112 foreach (string ${parsed_DOC} )
@@ -112,6 +115,9 @@ function(php_set)
112115
113116 set (condition TRUE )
114117 if (parsed_IF)
118+ # Make condition look nice in the possible output strings.
119+ string (STRIP "${parsed_IF} " parsed_IF)
120+ string (REGEX REPLACE "[ \t ]*[\r\n ]+[ \t\r\n ]*" "\n " parsed_IF "${parsed_IF} " )
115121 foreach (d ${parsed_IF} )
116122 cmake_language(EVAL CODE "
117123 if(${d} )
@@ -122,46 +128,120 @@ function(php_set)
122128 endforeach ()
123129 endif ()
124130
125- set (var "${ARGV0} " )
126- set (internal ___PHP_SET_${var} )
131+ set (varName "${ARGV0} " )
132+ set (bufferVarName ___PHP_SET_${varName} )
133+ set (bufferDoc "Internal storage for ${varName} variable" )
127134
128- if (NOT DEFINED ${internal } AND DEFINED ${var } )
135+ if (NOT DEFINED ${bufferVarName } AND DEFINED ${varName } )
129136 # Initial configuration phase with variable set by the user.
130- set (${internal } "${${var } }" CACHE INTERNAL "Internal storage for ${var } " )
131- elseif (NOT DEFINED ${internal } )
137+ set (${bufferVarName } "${${varName } }" CACHE INTERNAL "${bufferDoc } " )
138+ elseif (NOT DEFINED ${bufferVarName } )
132139 # Initial configuration phase without variable set by the user.
133- set (${internal } "${parsed_VALUE} " CACHE INTERNAL "Internal storage for ${var } " )
140+ set (${bufferVarName } "${parsed_VALUE} " CACHE INTERNAL "${bufferDoc } " )
134141 elseif (
135- DEFINED ${internal }
136- AND ${internal} _FORCED
137- AND NOT ${var } STREQUAL "${parsed_ELSE_VALUE} "
142+ DEFINED ${bufferVarName }
143+ AND ${bufferVarName} _OVERRIDDEN
144+ AND NOT ${varName } STREQUAL "${parsed_ELSE_VALUE} "
138145 )
139146 # Consecutive configuration phase that changes the variable after being
140147 # re-enabled.
141- set (${internal } "${${var } }" CACHE INTERNAL "Internal storage for ${var } " )
142- elseif (DEFINED ${internal } AND NOT ${internal} _FORCED )
148+ set (${bufferVarName } "${${varName } }" CACHE INTERNAL "${bufferDoc } " )
149+ elseif (DEFINED ${bufferVarName } AND NOT ${bufferVarName} _OVERRIDDEN )
143150 # Consecutive configuration phase.
144- set (${internal } "${${var } }" CACHE INTERNAL "Internal storage for ${var } " )
151+ set (${bufferVarName } "${${varName } }" CACHE INTERNAL "${bufferDoc } " )
145152 endif ()
146153
147154 if (condition )
148- set (${var } "${${internal } }" CACHE ${parsed_TYPE} "${doc} " FORCE)
155+ set (${varName } "${${bufferVarName } }" CACHE ${parsed_TYPE} "${doc} " FORCE)
149156 if (parsed_TYPE STREQUAL "STRING" AND parsed_CHOICES)
150- set_property (CACHE ${var } PROPERTY STRINGS ${parsed_CHOICES} )
157+ set_property (CACHE ${varName } PROPERTY STRINGS ${parsed_CHOICES} )
151158 endif ()
152- unset (${internal } CACHE )
153- unset (${internal} _FORCED CACHE )
159+ unset (${bufferVarName } CACHE )
160+ unset (${bufferVarName} _OVERRIDDEN CACHE )
154161 else ()
162+ _php_set_validate_input()
163+
155164 if (DEFINED parsed_ELSE_VALUE)
156- set (${var } "${parsed_ELSE_VALUE} " CACHE INTERNAL "${doc} " )
165+ set (${varName } "${parsed_ELSE_VALUE} " CACHE INTERNAL "${doc} " FORCE )
157166 else ()
158- unset (${var } CACHE )
167+ unset (${varName } CACHE )
159168 endif ()
160169 set (
161- ${internal} _FORCED
170+ ${bufferVarName} _OVERRIDDEN
162171 TRUE
163172 CACHE INTERNAL
164- "Internal marker that ${var} has a forced value ."
173+ "Internal marker that ${varName} is overridden ."
165174 )
166175 endif ()
167176endfunction ()
177+
178+ # Validate parsed arguments.
179+ function (_php_set_validate_arguments arguments)
180+ if (parsed_UNPARSED_ARGUMENTS)
181+ message (FATAL_ERROR "Bad arguments: ${parsed_UNPARSED_ARGUMENTS} " )
182+ endif ()
183+
184+ if (NOT DEFINED parsed_VALUE)
185+ message (FATAL_ERROR "Missing VALUE argument" )
186+ endif ()
187+
188+ if (NOT parsed_TYPE)
189+ message (FATAL_ERROR "Missing TYPE argument" )
190+ elseif (NOT parsed_TYPE MATCHES "^(BOOL|FILEPATH|PATH|STRING)$" )
191+ message (FATAL_ERROR "Unknown TYPE argument: ${parsed_TYPE} " )
192+ endif ()
193+
194+ list (FIND arguments ELSE_VALUE elseValueIndex)
195+ if (NOT DEFINED parsed_IF AND NOT elseValueIndex EQUAL -1)
196+ message (FATAL_ERROR "Redundant ELSE_VALUE argument without IF condition" )
197+ elseif (
198+ DEFINED parsed_IF
199+ AND NOT DEFINED parsed_ELSE_VALUE
200+ AND NOT elseValueIndex EQUAL -1
201+ )
202+ message (FATAL_ERROR "Missing ELSE_VALUE argument" )
203+ endif ()
204+
205+ if (NOT DEFINED parsed_DOC)
206+ message (FATAL_ERROR "Missing DOC argument" )
207+ endif ()
208+ endfunction ()
209+
210+ # Output warning when setting conditional variable and condition is not met.
211+ function (_php_set_validate_input)
212+ get_property (helpString CACHE ${varName} PROPERTY HELPSTRING )
213+ if (NOT helpString STREQUAL "No help, variable specified on the command line." )
214+ return ()
215+ endif ()
216+
217+ if (NOT parsed_WARNING)
218+ set (parsed_WARNING "Variable ${varName} " )
219+ if (DEFINED parsed_ELSE_VALUE)
220+ string (
221+ APPEND
222+ parsed_WARNING
223+ " has been overridden (${varName} =${parsed_ELSE_VALUE} )"
224+ )
225+ else ()
226+ string (
227+ APPEND
228+ parsed_WARNING
229+ " has been overridden to an undefined state"
230+ )
231+ endif ()
232+ string (
233+ APPEND
234+ parsed_WARNING
235+ " as it depends on the condition:\n "
236+ "${parsed_IF} \n "
237+ )
238+ endif ()
239+ set (warning "" )
240+ foreach (string ${parsed_WARNING} )
241+ string (APPEND warning "${string} " )
242+ endforeach ()
243+
244+ if (NOT ${varName} STREQUAL "${parsed_ELSE_VALUE} " )
245+ message (WARNING "${warning} " )
246+ endif ()
247+ endfunction ()
0 commit comments