Options

Options are CMake CACHE variables which parameterize a project, augmented for presentation to users and consistency checking. If an aspect of your project might need to be tuned by users, an option declaration is probably the most accessible way to expose it.

option

option(
  name    < | BOOL | PATH | FILEPATH | STRING | ENUM enum_values... >
  help_string
  [DEFAULT default_value]
  [MARK_AS_ADVANCED]
  [REQUIRES [IF condition [dependency required_value]...]...]
  [VALIDATE CODE validation_code]
  [ADD_COMPILE_DEFINITIONS]
)

Declare an option with the provided name.

< | BOOL | PATH | FILEPATH | STRING | ENUM enum_values... >

The type of the CACHE variable. Options which are not explicitly typed are implicitly of type BOOL.

ENUM is an alias for STRING which additionally specifies allowed values by setting the STRINGS property.

Every value (default, required values, …) associated with options of PATH or FILEPATH type are converted to normalized, native paths. Additionally, if such a value is a relative path then it is assumed to be relative to CMAKE_SOURCE_DIR and will be converted to an absolute path.

BOOL options are always validated to be either ON or OFF. ENUM options are always checked against their value set. Additional validation can be specified using VALIDATE CODE.

help_string

The help string of the CACHE variable.

An explanation of the option’s purpose, displayed in ccmake and other GUIs. Predefined macros for the option will be decorated with a doxygen-style comment containing this text (which should be accessible to your language server). It will also be displayed in the summary when configuration completes.

Note

An escaped representation of the help string is stored, which will be shown directly in ccmake and other viewers which only support single line help strings.

DEFAULT default_value

The option’s value if no explicit value is provided and no requirements constrain it.

Note

In fresh builds, environment variables can be used to assign to options. For example if an option named FOO_LEVEL is not otherwise defined but ENV{FOO_LEVEL} is defined, then the environment variable will be used instead of the default. (This can be disabled by setting ENV{MAUD_DISABLE_ENVIRONMENT_OPTIONS} = ON.)

Note

CACHE variables’ values may be defined before their declaration as an option (for example if the option is defined on the command line via -DFOO_LEVEL=HI) in which case the declaration will initialize other properties, leaving the value unchanged.

Implicit defaults

BOOL

OFF

PATH or FILEPATH

${CMAKE_SOURCE_DIR}

STRING

""

ENUM

the first enum value

MARK_AS_ADVANCED

Mark this CACHE variable advanced. Its value will not be displayed by default in the summary (it will be displayed if its value is non-default) and GUIs (ccmake for example provides an explicit toggle).

REQUIRES

Begin a set of requirement blocks. Each block begins with IF condition where condition is a possible value of the option. The block continues with a sequence of dependency required_value pairs where each dependency names another option. If the option’s value is resolved to condition, then each dependency in its block will be set to the corresponding required_value.

Note

A dependency need not be declared with option() before it is referenced in a requirement block.

Note

To simplify the common case of a BOOL option which only has requirements when it is ON, REQUIRES IF ON may be shortened to just REQUIRES.

VALIDATE CODE validation_code

Provide code to validate the option. The code block will be evaluated after requirements have been resolved and the option’s final value is known. For example this could be used to assert that a FILEPATH option specifies a readable file.

ADD_COMPILE_DEFINITIONS

If specified, macros will be added to the predefines buffer to expose option values to C++ code.

For a boolean option an identically named macro will be defined to 0 or 1

// FOO_EMULATED: BOOL
#define FOO_EMULATED 0

The name of an enumeration option will be concatenated with each potential value to get macro names, each of which are defined to 0 or 1

// FOO_LEVEL: ENUM LOW MED HI
#define FOO_LEVEL_LOW 0
#define FOO_LEVEL_MED 0
#define FOO_LEVEL_HI 1

For options of any other type an identically named macro will be defined to a string literal

// FOO_SOCKET_PATH: FILEPATH
#define FOO_SOCKET_PATH "/var/run/foo"

Options summary

After configuration is complete, a summary of option values is printed. The resolved value of each option is printed, along with the reason for that value and the option’s help string.

Groups of associated options can be declared by writing set(OPTION_GROUP "FOO-related options") before declaring the options. This adds a heading in the summary.

-- FOO-related options:
--
-- FOO_EMULATED = OFF [constrained by FOO_LEVEL]
--      Emulate FOO functionality rather than requesting a real FOO endpoint.
-- FOO_LEVEL = HI (of LOW;MED;HI) [user configured]
--      What level of FOO API should be requested.
--      LOW is primarily used for testing and is not otherwise recommended.
-- FOO_SOCKET_PATH = /var/run/foo [default]
--      Explicit socket for FOO endpoint.

As part of the options summary, a cmake configure preset is appended to CMakeUserPresets.json for easy copy-pasting, reproduction, etc.

Option Requirements and Resolution

Project options are frequently interdependent; for example enabling one feature might be impossible without enabling its dependencies. Manually resolving these interdependencies to a consistent state across all options in the project is frequently messy and error prone. option() integrates a solution to this problem in the REQUIRES argument. The requirements of each option can be specified in terms of assignments to other options on which it depends.

option(
  FOO_LEVEL ENUM LOW MED HI
  REQUIRES            # requirements of FOO_LEVEL follow
  IF HI               # if FOO_LEVEL is HI ...
    FOO_EMULATED OFF  # ... FOO_EMULATED must be set to OFF
)

The first time an option is accessed, its value and the values of its dependencies are resolved. This ensures all requirements are met (or reports an error if unsatisfiable requirements are encountered).

option(alpha BOOL DEFAULT ON REQUIRES beta 3)
option(beta ENUM 1 2 3)

# beta accessed; triggers resolution of alpha
#   no requirements on alpha; alpha resolved to ON
# alpha=ON requires beta=3; beta resolved to 3
if(beta EQUAL 1)
  # ...
endif()
option(alpha BOOL DEFAULT ON REQUIRES beta 3)
option(beta ENUM 1 2 3)
option(omega BOOL DEFAULT ON REQUIRES beta 1)

# beta accessed; triggers resolution of alpha and omega
#   no requirements on alpha or omega; both are resolved to ON

# CMake Error at /tmp/usr/lib/cmake/Maud/Maud.cmake:1455 (message):
#
#       Option constraint conflict: beta is constrained
#       by alpha to be
#         "3"
#       but omega requires it to be
#         "1"
if(beta EQUAL 1)
  # ...
endif()

Warning

Since option resolution is implemented using variable_watch, accessing an option’s value through $CACHE{FOO} or get_property(... CACHE FOO PROPERTY VALUE) will circumvent resolution.

Options are considered to form a directed acyclic graph: each option may declare a requirement on any other option as long as no cycles are formed. Options with no requirements placed on them will have their default or user configured value. Otherwise requirements determine the option’s value (even if the dependency’s default is required).

option(foo0 BOOL DEFAULT ON REQUIRES foo1 ON)
option(foo1 BOOL DEFAULT ON REQUIRES foo2 ON)
option(foo2 BOOL DEFAULT ON REQUIRES foo3 ON)
option(foo3 BOOL DEFAULT ON)

# foo3 accessed; triggers resolution of foo2
#   ... triggers resolution of foo1
#     ... triggers resolution of foo0
#       no requirements on foo0; foo0 resolved to ON
#     foo0=ON requires foo1=ON; foo1 resolved to ON
#   foo1=ON requires foo2=ON; foo2 resolved to ON
# foo2=ON requires foo3=ON; foo3 resolved to ON
if(foo3)
  # ...
endif()
option(foo0 BOOL DEFAULT ON REQUIRES foo1 ON)
option(foo1 BOOL DEFAULT ON REQUIRES foo2 ON)
option(foo2 BOOL DEFAULT ON REQUIRES foo3 ON)
option(foo3 BOOL DEFAULT ON REQUIRES foo0 ON)

# foo3 accessed; triggers resolution of foo2
#   ... triggers resolution of foo1
#     ... triggers resolution of foo0
#       ... re-enters resolution of foo3

# CMake Error at /tmp/usr/lib/cmake/Maud/Maud.cmake:1436 (message):
#
#       Circular constraint between options
#         foo3;foo2;foo1;foo0
if(foo3)
  # ...
endif()

Note

User provided values (via -DFOO=0 on the command line, through preset JSON, from an environment variable, …) are not considered a hard constraint and will always be overridden if necessary to satisfy declared requirements. On a fresh configuration it is possible to detect such an override and a warning will be issued to facilitate avoidance of inconsistent user provided values.

Conflicting requirements will result in a FATAL_ERROR. Likewise if a requirement is placed on an option which has been accessed, that requirement can only check for consistency with the option’s already resolved value. Whenever possible I recommend grouping declarations of interdependent options, which makes it easier to avoid this error.

option(alpha BOOL DEFAULT ON REQUIRES beta 3)
option(beta ENUM 1 2 3)

# beta accessed; triggers resolution of alpha
#   no requirements on alpha; alpha resolved to ON
# alpha=ON requires beta=3; beta resolved to 3
if(beta EQUAL 1)
  # ...
endif()
# alpha is declared later
option(beta ENUM 1 2 3)

# beta accessed; no requirements on beta; beta resolved to 1
if(beta EQUAL 1)
  setup_beta_feature(1)
endif()

# beta's value is already resolved by the access above
option(alpha BOOL DEFAULT ON REQUIRES beta 3)

# CMake Error at /tmp/usr/lib/cmake/Maud/Maud.cmake:1468 (message):
#
#       Option constraint conflict: beta was already resolved to
#         "1"
#       but alpha requires it to be
#         "3"