Variables¶
Setting, And Variable Names¶
Variable names are case-sensitive
set(some_variable value) message("${some_variable}") set(Some_Variable "different value") message("${Some_Variable}")
$ cmake -P set.cmake value different value
Cannot contain whitespace
set("some variable" value) message("${some variable}")
$ cmake -P set-whitespace.cmake CMake Warning (dev) at set-whitespace.cmake:2 (message): Syntax error in cmake code at /home/jfasch/My-Projects/jfasch-home/trainings/material/soup/cmake/advanced/language/variables/code/set-whitespace.cmake:2 when parsing string ${some variable} syntax error, unexpected cal_SYMBOL, expecting } (3) Policy CMP0010 is not set: Bad variable reference syntax is an error. Run "cmake --help-policy CMP0010" for policy details. Use the cmake_policy command to set the policy and suppress this warning. This warning is for project developers. Use -Wno-dev to suppress it. ${some variable}
Empty/Undefined Variables¶
Empty variable
set(some_variable "") # <--- empty value if ("${some_variable}" STREQUAL "") # <--- check if empty message("some_variable is empty") endif() if (DEFINED some_variable) # <--- check if defined message("some_variable is defined") endif()
$ cmake -P empty-variable.cmake some_variable is empty some_variable is defined
Undefined variable
if (NOT DEFINED some_variable) message("some_variable is not defined") endif() if ("${some_variable}" STREQUAL "") message("... although some_variable has an empty value") endif()
$ cmake -P undefined-variable.cmake some_variable is not defined ... although some_variable has an empty value
Removing Variables¶
Variables can be removed at runtime
Preferred: unset (less confusing to readers)
set(some_variable "value") if (DEFINED some_variable) message("some_variable is defined") endif() unset(some_variable) # <--- dedicated unset() if (NOT DEFINED some_variable) message("some_variable undefined") endif()
$ cmake -P unset-variable-unset.cmake some_variable is defined some_variable undefined
set with no value
set(some_variable "value") if (DEFINED some_variable) message("some_variable is defined") endif() set(some_variable) # <--- unset as side-effect # <--- of set if (NOT DEFINED some_variable) message("some_variable is undefined") endif()
$ cmake -P unset-variable-set.cmake some_variable is defined some_variable is undefined
Checking Emptyness And Definedness¶
Emptyness and definedness are often considered equivalent but aren’t - there’s a subtle difference.
Checking string length
set(some_variable "") # <--- empty value if (DEFINED some_variable) message("some_variable is defined") if ("${some_variable}" STREQUAL "") message("... although some_variable is empty") endif() endif()
$ cmake -P empty-variable-check.cmake some_variable is defined ... although some_variable is empty
Indirect Variables¶
Variables contain strings
Some variables contain variable names
set(some_variable "a value") set(another_variable "another value") set(name_of_variable "some_variable") message("direct: ${name_of_variable}") message("indirect: ${${name_of_variable}}") # <--- double expansion set(name_of_variable "another_variable") message("indirect: ${${name_of_variable}}") # <--- double expansion
$ cmake -P indirect-variable.cmake direct: some_variable indirect: a value indirect: another value
Danger: Lookup Scope¶
There are multiple kinds of variables,
Normal variables, referenced with
${NAME}
Environment variables, referenced with
$ENV{NAME}
. Environment variables are provided by the operating system. See Environment Variables, and also Environment (Variables))Cache variables, referenced with
$CACHE{NAME}
. In short, cache variable are used to store variables persistently. See Cache Variables.
Attention
It’s not always clear what you get when you say ${NAME}
though!
See Scopes: Function Scope.
Environment Variables¶
Environment are an OS concept
Inherited from parent to child process
Modification only affects child processes
⟶ no subsequent cmake runs!
Usage
Accessing environment variables
message("Your PATH environment variable is: >>>$ENV{PATH}<<<")
$ cmake -P env-print-path.cmake Your PATH environment variable is: >>>/home/jfasch/My-Environments/jfasch-home/bin:/home/jfasch/cross/bin:/home/jfasch/x-tools/armv8-rpi4-linux-gnueabihf/bin:/home/jfasch/HomeBrain/bin:/home/jfasch/cross/bin:/home/jfasch/x-tools/armv8-rpi4-linux-gnueabihf/bin:/home/jfasch/HomeBrain/bin:/home/jfasch/.local/bin:/home/jfasch/bin:/usr/lib64/ccache:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin<<<
Setting environment variables. Because environment variables are inherited to child processes, those are only visible by children created during the CMake run.
set(ENV{SOME_VARIABLE} "value") execute_process(COMMAND sh "-c" "echo child: SOME_VARIABLE=\$SOME_VARIABLE")
$ cmake -P env-set.cmake child: SOME_VARIABLE=value
Cache Variables¶
Persistent variables
Visible in a project, across CMake runs
⟶
CMakeCache.txt
in the build directory
cmake_minimum_required(VERSION 3.20)
project(cache-demo)
if (NOT DEFINED some_variable)
message("setting some_variable the first time")
set(some_variable "value" CACHE STRING "Some variable")
endif()
message("Normal variable: ${some_variable}")
message("Cache variable: $CACHE{some_variable}")
First run
$ mkdir /tmp/cmake-demo $ cmake -B /tmp/cmake-demo/ -S ./code/project-cache/ ... setting some_variable the first time Normal variable: value Cache variable: value ...
Second run
$ cmake -B /tmp/cmake-demo/ -S ./code/project-cache/ ... Normal variable: value Cache variable: value ...
Scopes: Function Scope¶
Local variables
function(some_function) set(local_variable "local value") endfunction() some_function() if (NOT DEFINED local_variable) message("behavior as usual") endif()
$ cmake -P scopes-function-local-scope.cmake behavior as usual
Global variables (well, and directory scope?)
set(global_variable "global value") function(some_function) message("some_function: \${global_variable}: ${global_variable}") endfunction() some_function()
$ cmake -P scopes-function-global-scope.cmake some_function: ${global_variable}: global value
Intermediate scope. Calling function inherits its variables into called function.
function(inner_function) message("inner_function: \${outer_variable}: ${outer_variable}") endfunction() function(outer_function) set(outer_variable "outer value") inner_function() endfunction() outer_function()
$ cmake -P scopes-function-local-scope-longer-call-chain.cmake inner_function: ${outer_variable}: outer value
Scopes: Directory Scope¶
Variables are not only “inherited” across function
Also across directories
Given the project structure,
directory-scope/
├── CMakeLists.txt
└── subdirectory
└── CMakeLists.txt
Lets say the top level CMakeLists.txt
has
set(some_variable "value")
add_subdirectory(subdirectory)
and subdirectory/CMakeLists.txt
has
message("subdirectory: \${some_variable}: ${some_variable}")
A CMake run would print,
$ cmake -B /tmp/cmake-demo/ -S ./code/directory-scope
...
subdirectory: ${some_variable}: value
...