Browse Source

CMakeDependentOption: Introduce policy CMP0127 for full Condition Syntax

Fixes: #22303
Daniel Schürmann 4 years ago
parent
commit
059b90a0b4

+ 8 - 0
Help/manual/cmake-policies.7.rst

@@ -51,6 +51,14 @@ The :variable:`CMAKE_MINIMUM_REQUIRED_VERSION` variable may also be used
 to determine whether to report an error on use of deprecated macros or
 functions.
 
+Policies Introduced by CMake 3.22
+=================================
+
+.. toctree::
+   :maxdepth: 1
+
+   CMP0127: cmake_dependent_option() supports full Condition Syntax. </policy/CMP0127>
+
 Policies Introduced by CMake 3.21
 =================================
 

+ 32 - 0
Help/policy/CMP0127.rst

@@ -0,0 +1,32 @@
+CMP0127
+-------
+
+:command:`cmake_dependent_option` supports full :ref:`Condition Syntax`.
+
+The ``<depends>`` parameter accepts a :ref:`semicolon-separated list <CMake
+Language Lists>` of conditions.  CMake 3.21 and lower evaluates each
+``condition`` as ``if(${condition})``, which does not properly handle
+conditions with nested paren groups.  CMake 3.22 and above instead prefer
+to evaluate each ``condition`` as ``if(<condition>)``, where ``<condition>``
+is re-parsed as if literally written in a call to :command:`if`.  This
+allows expressions like::
+
+  "A AND (B OR C)"
+
+but requires expressions like::
+
+  "FOO MATCHES (UPPER|lower)"
+
+to be re-written as::
+
+  "FOO MATCHES \"(UPPER|lower)\""
+
+Policy ``CMP0127`` provides compatibility for projects that have not
+been updated to expect the new behavior.
+
+This policy was introduced in CMake version 3.22.  CMake version
+|release| warns when the policy is not set and uses ``OLD`` behavior.
+Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
+explicitly.
+
+.. include:: DEPRECATED.txt

+ 6 - 0
Help/release/dev/cmake_dependent_option_policy.rst

@@ -0,0 +1,6 @@
+cmake_dependent_option_policy
+-----------------------------
+
+* The :module:`CMakeDependentOption` module :command:`cmake_dependent_option`
+  macro now supports full :ref:`Condition Syntax`.
+  See policy :policy:`CMP0127`.

+ 42 - 19
Modules/CMakeDependentOption.cmake

@@ -10,44 +10,62 @@ Macro to provide an option dependent on other options.
 This macro presents an option to the user only if a set of other
 conditions are true.
 
-Usage:
+.. command:: cmake_dependent_option
 
-.. code-block:: cmake
+  .. code-block:: cmake
 
-  cmake_dependent_option(<option> "<help_text>" <value> <depends> <force>)
+    cmake_dependent_option(<option> "<help_text>" <value> <depends> <force>)
 
-Where ``<option>`` is available to the user if ``<depends>`` is true. When
-``<option>`` is available, the given ``<help_text>`` and initial ``<value>``
-are used. If the ``<depends>`` condition is not true, ``<option>`` will not be
-presented and will always have the value given by ``<force>``. Any value set by
-the user is preserved for when the option is presented again. Each element in
-the fourth parameter is evaluated as an if-condition, so
-:ref:`Condition Syntax` can be used.
+  Makes ``<option>`` available to the user if ``<depends>`` is true. When
+  ``<option>`` is available, the given ``<help_text>`` and initial ``<value>``
+  are used. If the ``<depends>`` condition is not true, ``<option>`` will not be
+  presented and will always have the value given by ``<force>``. Any value set by
+  the user is preserved for when the option is presented again. In case ``<depends>``
+  is a :ref:`semicolon-separated list <CMake Language Lists>`, all elements must
+  be true in order to initialize ``<option>`` with ``<value>``.
 
 Example invocation:
 
 .. code-block:: cmake
 
-  cmake_dependent_option(USE_FOO "Use Foo" ON
-                         "USE_BAR;NOT USE_ZOT" OFF)
+  cmake_dependent_option(USE_FOO "Use Foo" ON "USE_BAR;NOT USE_ZOT" OFF)
 
 If ``USE_BAR`` is true and ``USE_ZOT`` is false, this provides an option called
 ``USE_FOO`` that defaults to ON. Otherwise, it sets ``USE_FOO`` to OFF and
 hides the option from the user. If the status of ``USE_BAR`` or ``USE_ZOT``
 ever changes, any value for the ``USE_FOO`` option is saved so that when the
 option is re-enabled it retains its old value.
+
+.. versionadded:: 3.22
+
+  Full :ref:`Condition Syntax` is now supported.  See policy :policy:`CMP0127`.
+
 #]=======================================================================]
 
 macro(CMAKE_DEPENDENT_OPTION option doc default depends force)
+  cmake_policy(GET CMP0127 _CDO_CMP0127
+    PARENT_SCOPE # undocumented, do not use outside of CMake
+    )
   if(${option}_ISSET MATCHES "^${option}_ISSET$")
     set(${option}_AVAILABLE 1)
-    foreach(d ${depends})
-      string(REGEX REPLACE " +" ";" CMAKE_DEPENDENT_OPTION_DEP "${d}")
-      if(${CMAKE_DEPENDENT_OPTION_DEP})
-      else()
-        set(${option}_AVAILABLE 0)
-      endif()
-    endforeach()
+    if("x${_CDO_CMP0127}x" STREQUAL "xNEWx")
+      foreach(d ${depends})
+        cmake_language(EVAL CODE "
+          if (${d})
+          else()
+            set(${option}_AVAILABLE 0)
+          endif()"
+        )
+      endforeach()
+    else()
+      foreach(d ${depends})
+        string(REGEX REPLACE " +" ";" CMAKE_DEPENDENT_OPTION_DEP "${d}")
+        if(${CMAKE_DEPENDENT_OPTION_DEP})
+        else()
+          set(${option}_AVAILABLE 0)
+        endif()
+      endforeach()
+    endif()
     if(${option}_AVAILABLE)
       option(${option} "${doc}" "${default}")
       set(${option} "${${option}}" CACHE BOOL "${doc}" FORCE)
@@ -61,4 +79,9 @@ macro(CMAKE_DEPENDENT_OPTION option doc default depends force)
   else()
     set(${option} "${${option}_ISSET}")
   endif()
+  if("x${_CDO_CMP0127}x" STREQUAL "xx" AND "x${depends}x" MATCHES "[^A-Za-z0-9_; ]")
+    cmake_policy(GET_WARNING CMP0127 _CDO_CMP0127_WARNING)
+    message(AUTHOR_WARNING "${_CDO_CMP0127_WARNING}")
+  endif()
+  unset(_CDO_CMP0127)
 endmacro()

+ 4 - 1
Source/cmPolicies.h

@@ -379,7 +379,10 @@ class cmMakefile;
          3, 21, 0, cmPolicies::WARN)                                          \
   SELECT(POLICY, CMP0126,                                                     \
          "set(CACHE) does not remove a normal variable of the same name.", 3, \
-         21, 0, cmPolicies::WARN)
+         21, 0, cmPolicies::WARN)                                             \
+  SELECT(POLICY, CMP0127,                                                     \
+         "cmake_dependent_option() supports full Condition Syntax.", 3, 22,   \
+         0, cmPolicies::WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \

+ 0 - 0
Tests/RunCMake/CMakeDependentOption/Regex-stdout.txt → Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-NEW-stdout.txt


+ 9 - 0
Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-NEW.cmake

@@ -0,0 +1,9 @@
+include(CMakeDependentOption)
+
+cmake_policy(SET CMP0127 NEW)
+
+set(A 1)
+set(B 1)
+set(C 0)
+cmake_dependent_option(USE_FOO "Use Foo" ON "A AND (B OR C)" OFF)
+message(STATUS "USE_FOO='${USE_FOO}'")

+ 9 - 0
Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-WARN-stderr.txt

@@ -0,0 +1,9 @@
+^CMake Warning \(dev\) at [^
+]*/Modules/CMakeDependentOption.cmake:[0-9]+ \(message\):
+  Policy CMP0127 is not set: cmake_dependent_option\(\) supports full Condition
+  Syntax.  Run "cmake --help-policy CMP0127" for policy details.  Use the
+  cmake_policy command to set the policy and suppress this warning.
+Call Stack \(most recent call first\):
+  [^
+]*/Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-WARN.cmake:[0-9]+ \(cmake_dependent_option\)
+This warning is for project developers.  Use -Wno-dev to suppress it.$

+ 2 - 0
Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-WARN-stdout.txt

@@ -0,0 +1,2 @@
+-- USE_FOO='OFF'
+-- USE_BAR='ON'

+ 9 - 0
Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-WARN.cmake

@@ -0,0 +1,9 @@
+include(CMakeDependentOption)
+
+set(A 1)
+set(B 1)
+set(C 0)
+cmake_dependent_option(USE_FOO "Use Foo" ON "A AND (B OR C)" OFF)
+message(STATUS "USE_FOO='${USE_FOO}'")
+cmake_dependent_option(USE_BAR "Use Bar" ON "A;B" OFF)
+message(STATUS "USE_BAR='${USE_BAR}'")

+ 1 - 0
Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-NEW-stdout.txt

@@ -0,0 +1 @@
+-- USE_FOO='ON'

+ 7 - 0
Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-NEW.cmake

@@ -0,0 +1,7 @@
+include(CMakeDependentOption)
+
+cmake_policy(SET CMP0127 NEW)
+
+set(FOO "lower")
+cmake_dependent_option(USE_FOO "Use Foo" ON "FOO MATCHES \"(UPPER|lower)\"" OFF)
+message(STATUS "USE_FOO='${USE_FOO}'")

+ 1 - 0
Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-OLD-stdout.txt

@@ -0,0 +1 @@
+-- USE_FOO='ON'

+ 2 - 0
Tests/RunCMake/CMakeDependentOption/Regex.cmake → Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-OLD.cmake

@@ -1,5 +1,7 @@
 include(CMakeDependentOption)
 
+cmake_policy(SET CMP0127 OLD)
+
 set(FOO "lower")
 cmake_dependent_option(USE_FOO "Use Foo" ON "FOO MATCHES (UPPER|lower)" OFF)
 message(STATUS "USE_FOO='${USE_FOO}'")

+ 4 - 1
Tests/RunCMake/CMakeDependentOption/RunCMakeTest.cmake

@@ -1,3 +1,6 @@
 include(RunCMake)
 
-run_cmake_script(Regex)
+run_cmake_script(Regex-CMP0127-NEW)
+run_cmake_script(Regex-CMP0127-OLD)
+run_cmake_script(Parentheses-CMP0127-NEW)
+run_cmake_script(Parentheses-CMP0127-WARN)

+ 1 - 0
Tests/RunCMake/CMakeDependentOption/Simple-CMP0127-OLD-stdout.txt

@@ -0,0 +1 @@
+-- USE_FOO='ON'

+ 6 - 0
Tests/RunCMake/CMakeDependentOption/Simple-CMP0127-OLD.cmake

@@ -0,0 +1,6 @@
+include(CMakeDependentOption)
+
+set(A1 1)
+set(bb 1)
+cmake_dependent_option(USE_FOO "Use Foo" ON "A1;bb" OFF)
+message(STATUS "USE_FOO='${USE_FOO}'")