# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
# file LICENSE.rst or https://cmake.org/licensing for details.

#[=======================================================================[.rst:
CMakeDependentOption
--------------------

This module provides a command to define boolean options whose availability and
default values depend on specified conditions or other options.  This helps
maintain a clean configuration interface by only displaying options that are
relevant to the current settings.

Load this module in CMake with:

.. code-block:: cmake

  include(CMakeDependentOption)

Commands
^^^^^^^^

This module provides the following command:

.. command:: cmake_dependent_option

  Provides a boolean option that depends on a set of conditions:

  .. code-block:: cmake

    cmake_dependent_option(<variable> <help> <value> <condition> <else-value>)

  This command creates a boolean ``<variable>`` and makes it available to the
  user in the GUI (such as :manual:`cmake-gui(1)` or :manual:`ccmake(1)`), if
  a set of conditions evaluates to boolean true.

  The arguments are:

  ``<variable>``
    The name of a variable that stores the option value.

  ``<help>``
    A brief description of the option.  This string is typically a short line of
    text and is displayed in the GUI.

  ``<value>``
    Boolean value for the ``<variable>``, when ``<condition>`` evaluates to
    boolean true.

  ``<condition>``
    Specifies the conditions that determine whether ``<variable>`` is set and
    visible in the GUI.

    * If ``<condition>`` evaluates to boolean false, option is hidden from the
      user in the GUI, and a local variable ``<variable>`` is set to
      ``<else-value>``.

    * If ``<condition>`` evaluates to boolean true, a boolean cache variable
      named ``<variable>`` is created with default ``<value>``, and option is
      shown in the GUI, allowing the user to enable or disable it.

    * If ``<condition>`` later evaluates to boolean false (on consecutive
      configuration run),  option is hidden from the user in the GUI and the
      ``<variable>`` type is changed to an internal cache variable.  In that
      case a local variable of the same name is set to ``<else-value>``.

    * If ``<condition>`` becomes true again in consecutive configuration runs,
      the user's previously set value is preserved.

    The ``<condition>`` argument can be:

    * A single condition (such as a variable name).

    * A :ref:`semicolon-separated list <CMake Language Lists>` of multiple
      conditions.

    * .. versionadded:: 3.22
        A full :ref:`Condition Syntax` as used in an ``if(<condition>)`` clause.
        See policy :policy:`CMP0127`.  This enables using entire condition
        syntax (such as grouping conditions with parens and similar).

  ``<else-value>``
    The value assigned to a local variable named ``<variable>``, when
    ``<condition>`` evaluates to boolean false.

  In CMake project mode, boolean cache variables are created as explained
  above.  In CMake script mode, boolean variables are set instead.

Examples
^^^^^^^^

Example: Basic Usage
""""""""""""""""""""

Using this module in a project to conditionally set an option:

.. code-block:: cmake
  :caption: ``CMakeLists.txt``

  include(CMakeDependentOption)

  cmake_dependent_option(USE_SSL_GNUTLS "Use GnuTLS for SSL" ON USE_SSL OFF)

Example: Enabling/Disabling Dependent Option
""""""""""""""""""""""""""""""""""""""""""""

Extending the previous example, this demonstrates how the module allows
user-configurable options based on a condition during the configuration phase:

.. code-block:: cmake
  :caption: ``CMakeLists.txt``

  include(CMakeDependentOption)

  option(USE_SSL "Enable SSL in the project" OFF)

  cmake_dependent_option(USE_SSL_GNUTLS "Use GnuTLS for SSL" ON USE_SSL OFF)

  message(STATUS "USE_SSL: ${USE_SSL}")
  message(STATUS "USE_SSL_GNUTLS: ${USE_SSL_GNUTLS}")

On the first configuration run, a boolean cache variable ``USE_SSL`` is set to
OFF, and a local variable ``USE_SSL_GNUTLS`` is set to OFF:

.. code-block:: console

  $ cmake -B build-dir

  -- USE_SSL: OFF
  -- USE_SSL_GNUTLS: OFF

Running CMake with ``USE_SSL=ON`` sets both ``USE_SSL`` and ``USE_SSL_GNUTLS``
boolean cache variables to ON:

.. code-block:: console

  $ cmake -B build-dir -D USE_SSL=ON

  -- USE_SSL: ON
  -- USE_SSL_GNUTLS: ON

On a subsequent configuration run with ``USE_SSL=OFF``, ``USE_SSL_GNUTLS``
follows suit.  However, its value is preserved in the internal cache while being
overridden locally:

.. code-block:: console

  $ cmake -B build-dir -D USE_SSL=OFF

  -- USE_SSL: OFF
  -- USE_SSL_GNUTLS: OFF

Example: Semicolon-separated List of Conditions
"""""""""""""""""""""""""""""""""""""""""""""""

The ``<condition>`` argument can also be a semicolon-separated list of
conditions.  In the following example, if the variable ``USE_BAR`` is ON and
variable ``USE_ZOT`` is OFF, the option ``USE_FOO`` is available and defaults to
ON.  Otherwise, ``USE_FOO`` is set to OFF and hidden from the user.

If the values of ``USE_BAR`` or ``USE_ZOT`` change in the future configuration
runs, the previous value of ``USE_FOO`` is preserved so that when it becomes
available again, it retains its last set value.

.. code-block:: cmake
  :caption: ``CMakeLists.txt``

  include(CMakeDependentOption)

  cmake_dependent_option(USE_FOO "Use Foo" ON "USE_BAR;NOT USE_ZOT" OFF)

Example: Full Condition Syntax
""""""""""""""""""""""""""""""

As of CMake 3.22, ``cmake_dependent_option()`` supports full condition syntax.

In fhe following example, if the condition evaluates to true, the option
``USE_FOO`` is available and set to ON.  Otherwise, it is set to OFF and hidden
in the GUI.  The value of ``USE_FOO`` is preserved across configuration runs,
similar to the previous example.

.. code-block:: cmake
  :caption: ``CMakeLists.txt``

  include(CMakeDependentOption)

  cmake_dependent_option(USE_FOO "Use Foo" ON "USE_A AND (USE_B OR USE_C)" OFF)

Another example demonstrates how an option can be conditionally available based
on the target system:

.. code-block:: cmake
  :caption: ``CMakeLists.txt``

  include(CMakeDependentOption)

  cmake_dependent_option(
    ENABLE_FOO
    "Enable feature Foo (this option is available when building for Windows)"
    ON
    [[CMAKE_SYSTEM_NAME STREQUAL "Windows"]]
    OFF
  )

See Also
^^^^^^^^

* The :command:`option` command to provide a boolean option that the user can
  optionally select.
#]=======================================================================]

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)
    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)
    else()
      if(${option} MATCHES "^${option}$")
      else()
        set(${option} "${${option}}" CACHE INTERNAL "${doc}")
      endif()
      set(${option} ${force})
    endif()
  else()
    set(${option} "${${option}_ISSET}")
  endif()
  if("x${_CDO_CMP0127}x" STREQUAL "xx" AND "x${depends}x" MATCHES "[^A-Za-z0-9_.; ]")
    cmake_policy(ISSUE_WARNING CMP0127)
  endif()
  unset(_CDO_CMP0127)
endmacro()
