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
...