CMake from Beginner to Expert (2): Variables and Scope

1. Why is it important to understand variables and scope?

CMake is not just a build tool, but also a “meta-programming language”. Variables and scope are its core programming elements, directly affecting:

  • Project Configuration: Dynamically switching compilers, library paths, and compilation options

  • Module Interaction: Data transfer between parent and child directories

  • Environment Control: Cross-platform compatibility (Windows/Linux/macOS)

2. Basic Operations on Variables

2.1 <span>set()</span>: Defining Variables

Function Definition:

set(<variable> <value> [PARENT_SCOPE | CACHE <type> <docstring> [FORCE]])  

Regular Variables:

set(MY_NAME "CMake")  set(SRC_FILES main.cpp utils.cpp)  # List type  

Cache Variables (Globally Configurable):

set(USE_CUDA ON CACHE BOOL "Enable CUDA")  
  • <span>CACHE</span> Types: <span>BOOL</span>, <span>STRING</span>, <span>FILEPATH</span>, <span>INTERNAL</span>

  • <span>FORCE</span>: Force overwrite existing cache variables

2.2 <span>unset()</span>: Deleting Variables

unset(VAR_NAME [PARENT_SCOPE | CACHE])  

Scope Control:

unset(TEMP_VAR)          # Delete current scope variable  unset(CACHE_VAR CACHE)   # Delete cache variable  

2.3 <span>list()</span>: List Operations

list(APPEND/REMOVE_ITEM/GET/LENGTH ...)  

Common Operations:

set(SRC_LIST main.cpp)  list(APPEND SRC_LIST utils.cpp)  # Append element  list(REMOVE_ITEM SRC_LIST main.cpp)  # Remove element  list(LENGTH SRC_LIST list_len)   # Get length  

3. Environment Variable Management

3.1 Reading Environment Variables

  • Syntax: <span>$ENV{<variable>}</span>

message("Home directory: $ENV{HOME}")  if(DEFINED ENV{PATH})    message("PATH environment variable is defined")  endif()  

3.2 Setting Environment Variables

  • Effective only within the CMake process:

set(ENV{LD_LIBRARY_PATH} "/opt/cuda/lib64:$ENV{LD_LIBRARY_PATH}")  

Note: Modifications to environment variables in CMake do not affect the system environment and are only valid during the build process.

4. Deep Dive into Scope

4.1 Directory Scope

  • Rules: Each <span>CMakeLists.txt</span> and its <span>add_subdirectory()</span><span> create an independent scope</span>

  • Variable Passing: By default, variables are passed down, and need <span>PARENT_SCOPE</span> to pass up

# Parent directory CMakeLists.txt  set(PARENT_VAR "Hello")  add_subdirectory(subdir)  
# subdir/CMakeLists.txt  set(SUB_VAR "World" PARENT_SCOPE)  # Modify parent scope variable  

4.2 Function Scope

  • Rules: Variables set within a <span>function</span> are local by default, and need <span>PARENT_SCOPE</span> to pass to the caller

function(my_func)    set(LOCAL_VAR "Local" PARENT_SCOPE)  # Pass to parent scope  endfunction()  
my_func()  message("${LOCAL_VAR}")  # Outputs "Local"  

4.3 Global Scope

  • Cache Variables: Defined using the <span>CACHE</span> keyword, visible globally

set(GLOBAL_VAR "I'm global" CACHE STRING "")  

5. Practical Example: Dynamically Configuring Compiler Path

5.1 Scenario Requirements

  • Manually specify the GCC path for the ARM platform during cross-compilation

  • Allow users to override via cache variable in the command line

5.2 Implementation Code

# Define default compiler path  set(DEFAULT_ARM_GCC "/opt/arm-gcc/bin/arm-linux-gnueabihf-gcc")  
# Declare cache variable (user can override with -DARM_GCC_PATH=xxx)  set(ARM_GCC_PATH "${DEFAULT_ARM_GCC}" CACHE FILEPATH "ARM compiler path")  
# Verify if the compiler exists  if(NOT EXISTS "${ARM_GCC_PATH}")      message(FATAL_ERROR "ARM compiler not found: ${ARM_GCC_PATH}")  endif()  
# Set C/C++ compiler  set(CMAKE_C_COMPILER "${ARM_GCC_PATH}")  set(CMAKE_CXX_COMPILER "${ARM_GCC_PATH++}")  # Assume C++ compiler is g++  

5.3 Usage

# Command line to override compiler path  cmake -B build -DARM_GCC_PATH=/custom/arm-gcc/bin/gcc  

6. Scope Traps and Debugging Techniques

6.1 Common Issues

  • Variable Overwriting: Unexpected modification of parent directory variables in subdirectories

  • Cache Variable Pollution: Misuse of <span>FORCE</span> leading to uncontrollable configurations

6.2 Debugging Commands

  • Print Variable Value:

message("Variable value: ${MY_VAR}")  

View All Variables:

cmake -LA  # List all cache variables  

7. Summary and Next Article Preview

Key Points of This Article:

  • Master the advanced usage of <span>set()</span><span>, </span><code><span>unset()</span>, and <span>list()</span>

  • Understand the differences between directory, function, and global scope

  • Implement dynamic compiler configuration

Next Article Preview:

  • “Part 3: Functions and Parameter Passing – The Core of CMake Scripting”

    • <span>function()</span> vs <span>macro()</span> differences

    • Handling variable arguments with <span>ARGN</span>

    • Encapsulating cross-platform compilation functions

      CMake from Beginner to Expert (1): CMake Simplified Introduction – Environment Configuration and First Project

Leave a Comment