Skip to main content

Updated Jun 4, 2026

CMake Variables

What are Variables?​

Variables in CMake store strings, lists, or paths. They're the primary way to store and pass information in your build configuration.

Basic Variable Operations​

Setting Variables​

# Simple string
set(MY_VAR "Hello")

# Number (stored as string)
set(VERSION_MAJOR 1)

# Multiple values (creates a list)
set(SOURCES main.cpp utils.cpp helper.cpp)

# With CACHE (persists between runs)
set(BUILD_TYPE "Release" CACHE STRING "Build type")

Using Variables​

set(GREETING "Hello")
message(STATUS "Greeting: ${GREETING}")

# Output: -- Greeting: Hello
Dereferencing

Use ${VAR_NAME} to access the value. Without ${}, you get the literal string.

Unsetting Variables​

unset(MY_VAR)
unset(CACHE_VAR CACHE) # Remove from cache

Variable Scope​

Normal Variables (Function/Directory Scope)​

set(LOCAL_VAR "value") # Only in current scope

function(my_function)
set(FUNC_VAR "inside") # Only in function
message(STATUS ${FUNC_VAR}) # Works
endfunction()

message(STATUS ${FUNC_VAR}) # Undefined!

PARENT_SCOPE​

function(set_parent)
set(MY_VAR "value" PARENT_SCOPE)
endfunction()

set_parent()
message(STATUS ${MY_VAR}) # "value"

Cache Variables (Global/Persistent)​

set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libraries")

Cache variables:

  • Persist in CMakeCache.txt
  • Can be overridden from command line
  • Visible in GUI tools (ccmake, cmake-gui)

Override from command line:

Terminal
cmake -DBUILD_SHARED_LIBS=ON ..

Built-in Variables​

Project Variables​

project(MyProject VERSION 1.2.3)

message(STATUS "Name: ${PROJECT_NAME}") # MyProject
message(STATUS "Version: ${PROJECT_VERSION}") # 1.2.3
message(STATUS "Major: ${PROJECT_VERSION_MAJOR}") # 1
message(STATUS "Source: ${PROJECT_SOURCE_DIR}") # /path/to/source
message(STATUS "Binary: ${PROJECT_BINARY_DIR}") # /path/to/build

Common CMake Variables​

VariableDescriptionExample Value
CMAKE_SOURCE_DIRTop-level source directory/home/user/project
CMAKE_BINARY_DIRTop-level build directory/home/user/project/build
CMAKE_CURRENT_SOURCE_DIRCurrent CMakeLists.txt location/home/user/project/src
CMAKE_CURRENT_BINARY_DIRCurrent build subdirectory/home/user/project/build/src
CMAKE_CURRENT_LIST_DIRDirectory of current list file/home/user/project/cmake
Use Current Variables

In subdirectories, prefer CMAKE_CURRENT_SOURCE_DIR over CMAKE_SOURCE_DIR

Compiler Variables​

message(STATUS "C++ Compiler: ${CMAKE_CXX_COMPILER}")
message(STATUS "Compiler ID: ${CMAKE_CXX_COMPILER_ID}")
message(STATUS "Compiler Version: ${CMAKE_CXX_COMPILER_VERSION}")

Common CMAKE_CXX_COMPILER_ID values:

  • GNU - GCC
  • Clang - Clang/LLVM
  • MSVC - Microsoft Visual C++
  • AppleClang - Apple's Clang

System Variables​

message(STATUS "System: ${CMAKE_SYSTEM_NAME}")
message(STATUS "Processor: ${CMAKE_SYSTEM_PROCESSOR}")

if(WIN32)
message(STATUS "Windows detected")
elseif(UNIX)
message(STATUS "Unix-like system")
if(APPLE)
message(STATUS "macOS detected")
elseif(LINUX) # Not standard, use: ${CMAKE_SYSTEM_NAME} STREQUAL "Linux"
message(STATUS "Linux detected")
endif()
endif()

Build Configuration Variables​

# Build type
set(CMAKE_BUILD_TYPE Release)
# Options: Debug, Release, RelWithDebInfo, MinSizeRel

# Output directories
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

List Variables​

Lists are semicolon-separated strings.

Creating Lists​

# Method 1: Multiple arguments
set(SOURCES main.cpp utils.cpp)

# Method 2: Explicit semicolons
set(SOURCES "main.cpp;utils.cpp")

# Method 3: Append
set(SOURCES main.cpp)
list(APPEND SOURCES utils.cpp helper.cpp)

List Operations​

set(MY_LIST a b c d e)

# Length
list(LENGTH MY_LIST len)
message(STATUS "Length: ${len}") # 5

# Get element
list(GET MY_LIST 2 third_element)
message(STATUS "3rd: ${third_element}") # c

# Append
list(APPEND MY_LIST f g) # a b c d e f g

# Remove item
list(REMOVE_ITEM MY_LIST c) # a b d e

# Remove duplicates
list(REMOVE_DUPLICATES MY_LIST)

# Reverse
list(REVERSE MY_LIST)

# Sort
list(SORT MY_LIST)

# Find
list(FIND MY_LIST "d" index)
if(index EQUAL -1)
message(STATUS "Not found")
else()
message(STATUS "Found at index ${index}")
endif()
Quoting Lists

When passing lists to commands, don't quote them:

# Correct
add_executable(myapp ${SOURCES})

# Wrong - treated as single argument
add_executable(myapp "${SOURCES}")

String Operations​

String Manipulation​

set(STR "Hello World")

# Length
string(LENGTH "${STR}" len)

# Substring
string(SUBSTRING "${STR}" 0 5 result) # "Hello"

# Replace
string(REPLACE "World" "CMake" result "${STR}") # "Hello CMake"

# To upper/lower
string(TOUPPER "${STR}" upper) # "HELLO WORLD"
string(TOLOWER "${STR}" lower) # "hello world"

# Strip whitespace
string(STRIP " spaces " stripped) # "spaces"

Regular Expressions​

set(VERSION "v1.2.3-beta")

# Match
string(REGEX MATCH "v([0-9]+)\\.([0-9]+)" match "${VERSION}")
message(STATUS "Match: ${match}") # v1.2
message(STATUS "Major: ${CMAKE_MATCH_1}") # 1
message(STATUS "Minor: ${CMAKE_MATCH_2}") # 2

# Replace
string(REGEX REPLACE "v([0-9.]+).*" "\\1" clean_version "${VERSION}")
message(STATUS "Clean: ${clean_version}") # 1.2.3

Environment Variables​

Reading​

# Get environment variable
message(STATUS "PATH: $ENV{PATH}")
message(STATUS "HOME: $ENV{HOME}")

# Store in CMake variable
set(USER_HOME $ENV{HOME})

Setting​

# Set for current CMake run only
set(ENV{MY_VAR} "value")

# Note: Does NOT persist outside CMake
Temporary Only

set(ENV{...}) only affects the CMake process, not the system or build process.

Practical Examples​

Version Header Generation​

project(MyApp VERSION 1.2.3)

configure_file(
"${PROJECT_SOURCE_DIR}/version.h.in"
"${PROJECT_BINARY_DIR}/version.h"
)
version.h.in
#ifndef VERSION_H
#define VERSION_H

#define VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define VERSION_MINOR @PROJECT_VERSION_MINOR@
#define VERSION_PATCH @PROJECT_VERSION_PATCH@
#define VERSION_STRING "@PROJECT_VERSION@"

#endif

Generated version.h:

#ifndef VERSION_H
#define VERSION_H

#define VERSION_MAJOR 1
#define VERSION_MINOR 2
#define VERSION_PATCH 3
#define VERSION_STRING "1.2.3"

#endif

Platform-Specific Configuration​

if(WIN32)
set(PLATFORM_SOURCES src/windows.cpp)
set(PLATFORM_LIBS ws2_32)
elseif(UNIX)
set(PLATFORM_SOURCES src/unix.cpp)
set(PLATFORM_LIBS pthread)
endif()

add_executable(myapp
src/main.cpp
${PLATFORM_SOURCES}
)

target_link_libraries(myapp PRIVATE ${PLATFORM_LIBS})

Compiler-Specific Flags​

if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(WARNINGS -Wall -Wextra -Wpedantic)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(WARNINGS /W4)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(WARNINGS -Wall -Wextra -Wpedantic)
endif()

target_compile_options(myapp PRIVATE ${WARNINGS})

Variable Naming Conventions​

Best Practices
  • UPPERCASE_WITH_UNDERSCORES - Cache variables, important settings
  • MixedCase or lowercase_with_underscores - Local variables
  • _ prefix - Private/internal variables
  • Project prefix - MYPROJECT_OPTION_NAME
# Good
set(MYPROJECT_BUILD_TESTS ON CACHE BOOL "Build tests")
set(source_files main.cpp utils.cpp)

# Avoid
set(BuildTests ON) # Unclear scope
set(x main.cpp) # Unclear purpose

Common Pitfalls​

Unquoted Variables​

set(MY_VAR "value with spaces")

# Wrong - split into multiple arguments
message(STATUS ${MY_VAR})

# Correct
message(STATUS "${MY_VAR}")

Variable Expansion in Lists​

set(LIST_A a b c)
set(LIST_B ${LIST_A} d e)

message(STATUS "${LIST_B}") # a;b;c;d;e

Conditional Comparisons​

set(MY_VAR "ON")

# All these are equivalent:
if(MY_VAR)
if(${MY_VAR})
if("${MY_VAR}")

# String comparison:
if(MY_VAR STREQUAL "ON")
if("${MY_VAR}" STREQUAL "ON")

Quick Reference​

# Set
set(VAR "value")
set(VAR "value" CACHE TYPE "description")
set(VAR "value" PARENT_SCOPE)

# Unset
unset(VAR)

# Lists
list(APPEND VAR item1 item2)
list(LENGTH VAR len)
list(GET VAR index result)

# Strings
string(TOUPPER "${VAR}" upper)
string(REPLACE "old" "new" result "${VAR}")

# Check if defined
if(DEFINED VAR)
if(DEFINED CACHE{VAR})
if(DEFINED ENV{VAR})