Skip to main content

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})