Understanding Variables in CMake

Variables are an important component of CMake scripts, used to control various aspects of the build process. From specifying compiler options to setting installation paths and managing dependencies, the influence of variables can be seen everywhere.

This article summarizes the concepts and usage of variables based on the official documentation.

Basic Concepts

When we mention variables, we generally think of data types. However, in CMake, variables do not have strict data types; they are more like strings or undefined. Rather than talking about the data type of a variable, it is more appropriate to discuss its purpose. Common uses of variables include:

  1. 1. Strings: This is the most common case. When you set a variable, it is a string by default.
set(MY_VAR "Hello, World!")
  1. 2. Boolean Values: Represented by specific strings indicating true or false.
# Typically TRUE, ON, YES, Y, 1 are considered true
# while FALSE, OFF, NO, N, 0, IGNORED, NOTFOUND, empty strings are considered false.
set(IS_DEBUG TRUE)
if(IS_DEBUG)
  message("Debug mode is on.")
endif()
  1. 3. Paths: This variable is a special string used to specify the location of files or directories.
set(EXECUTABLE_OUTPUT_PATH "${PROJECT_BINARY_DIR}/bin")
  1. 4. Lists: Essentially strings separated by semicolons (or spaces), you can list multiple items in commands using semicolons (or spaces).
set(SOURCES1 main.cpp utils.cpp)
message("${SOURCES1}")
set(SOURCES2 main.cpp;utils.cpp)
message("${SOURCES2}")
  1. 5. Numbers: Treated as strings, but in some cases (such as mathematical operations), they are interpreted as numeric values.
math(EXPR RESULT "${NUMBER} + 5")
  1. 6. Environment Variables: CMake can access operating system environment variables.
if(DEFINED ENV{MY_ENV_VAR})
  message("Environment variable MY_ENV_VAR is defined.")
endif()

Variables have their own scope and cannot be accessed outside of that scope.

Normal Variable Definition

CMake has many predefined variables (not elaborated in this article; details will be provided when needed), and the official documentation gives detailed descriptions of these variables which users can directly use:

https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html
https://cmake.org/cmake/help/latest/manual/cmake-env-variables.7.html

For normal variables (equivalent to local variables, with scope limitations within the same CMake project), users can define them using the set command. Variable names are case-sensitive, and the general form is as follows:

# Set Normal Variable
set(<variable> <value>... [PARENT_SCOPE])
# <value>... accepts zero or more parameters; when multiple parameters are passed, they will be converted into a list</value></value></variable>

To reference a variable, use the following form:

${variable}

If the referenced variable is not defined, it returns an empty string. Typical examples are as follows:

cmake_minimum_required(VERSION 3.16)
project(test_var)

# Referencing an undefined variable returns an empty string
message("undefine_var={undefine_var}")# undefine_var={undefine_var}
# Regular example
set(var1 "hello world")
message("var1=${var1}")# var1=hello world
message("var1=" ${var1})# var1=hello world
message("\${var1}=" ${var1})# ${var1}=hello world
# Concatenation
message("${var1}_today")# hello world_today
# Nested example
set(var2 "var1")
message("\${var2}=" ${var2})# ${var2}=var1
message("\${${var2}}=" ${${var2}})# ${var1}=hello world

# Multiple parameters example: treating myVar as a list
set(myVar "a" "b" "c")# three parameters, separated by spaces
message("myVar=" ${myVar})# abc
message("myVar=${myVar}")# a;b;c
list(LENGTH myVar length)
message("myVar length = ${length}")# 3

set(myVar a b c)# three parameters, separated by spaces
message("myVar=" ${myVar})# abc
message("myVar=${myVar}")# a;b;c
list(LENGTH myVar length)
message("myVar length = ${length}")# 3

set(myVar a;b;c)# three parameters, separated by semicolons
message("myVar=" ${myVar})# abc
message("myVar=${myVar}")# a;b;c
list(LENGTH myVar length)
message("myVar length = ${length}")# 3

set(myVar "a b c")# one parameter
message("myVar=" ${myVar})# a b c
message("myVar=${myVar}")# a b c
list(LENGTH myVar length)
message("myVar length = ${length}")# 1

set(myVar a b;c)# three parameters, separated by semicolons and spaces
message("myVar=" ${myVar})# abc
message("myVar=${myVar}")# a;b;c
list(LENGTH myVar length)
message("myVar length = ${length}")# 3

set(myVar a "b c")# two parameters, separated by spaces
message("myVar=" ${myVar})# ab c
message("myVar=${myVar}")# a;b c
list(LENGTH myVar length)
message("myVar length = ${length}") # 2

Note that when printing a list, <span>message("myVar=" ${myVar})</span> will omit the semicolon between parameters, while <span>message("myVar=${myVar}")</span> will separate each parameter with a semicolon, making the second method clearer.

Env Variable Definition

If users need to define custom environment variables, they should follow this format:

# Set Environment Variable
set(ENV{<variable>} [<value>])</value></variable>

Typical examples are as follows:

set(ENV{PATH} "/opt/myDir")   # Environment variables are defined in the form of ENV{<variable>}
message("path=$ENV{PATH}")    # Environment variable references follow the form $ENV{<variable>} </variable></variable>

Cache Variable Definition

Cache variables are akin to global variables, with the following characteristics:

  1. 1. Persistence: Cache variables are saved in the CMakeCache.txt file, allowing them to remain consistent across different CMake runs.
  2. 2. Editability: Users can modify cache variables via command line or graphical interfaces (like CMake GUI).
  3. 3. Type System: Each cache variable has a type (such as PATH, FILEPATH, STRING, BOOL, etc.), which helps provide a better user experience and input validation.
  4. 4. Default Values: Default values can be set for cache variables. If users do not provide specific values, CMake will use these defaults.

Creating and setting cache variables requires the following format:

# Set Cache Entry
set(<variable> <value>... CACHE <type> <docstring> [FORCE])</docstring></type></value></variable>

Typical examples are as follows:

set(CMAKE_INSTALL_PREFIX "/usr/local" CACHE PATH "Install path")
message(STATUS "Install prefix is: ${CMAKE_INSTALL_PREFIX}")

For cache variables, there is a good article that can be referenced:

https://www.cnblogs.com/Braveliu/p/15614013.html

Bool Variable

For boolean variables, CMake provides the option command to set them, as follows:

# Note that help_text is mandatory! Without it, the variable will be set to OFF
option(<variable> "<help_text>" [value]) </help_text></variable>

If no initial value is provided, it defaults to OFF. If the variable was previously set as a normal or cache variable, using the option command to set it again will have no effect:

set(Var3 "hello")
option(Var3 "Var3 is bool" TRUE)   # No effect
message("${Var3}")

Variable Unsetting

Generally, we use the unset command to remove variables, in the following form:

# Unset Normal Variable or Cache Entry
unset(<variable> [CACHE | PARENT_SCOPE])
# Unset Environment Variable
unset(ENV{<variable>})</variable></variable>

Of course, we can also use set to unset a variable, in which case the set command is not followed by parameters, as shown in the example:

set(myVar)
message("myVar=${myVar}") # becomes an empty string

Summary

  1. 1. CMake variables are an important part of the build process, used to store and pass various configuration information. Proper use of variables can make CMake scripts more flexible and help better manage and configure CMake projects.

Leave a Comment