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