工具使用—CMake文件中的常见变量

一、CMake变量的常见类型

按照CMake官方文档的说法,其预定义的变量可分为很多种,比如路径变量、项目变量、编译器变量等等(有兴趣可以查看一下其官方文档)。但实际情况中,大多数的开发不可能对所有的相关细节进行处理,那个不用学了也就忘记了。所以大家只要记住几种主要的类型即可。可以CMake中的变量分成几种常见的类型:普通变量和缓存变量、参数变量、环境变量和命令行变量。下面针对其进行分别的说明

1. 普通变量和缓存变量

普通变量只存在于当前 CMake 配置过程中,受作用域影响,不会自动写入 CMakeCache.txt

cmake 复制代码
set(MY_NAME "ivus")
message("MY_NAME=${MY_NAME}")

应用:

cmake 复制代码
set(SRC_FILES main.cpp app.cpp)
add_executable(app ${SRC_FILES})

它一般用在于当前CMake配置过程,受到目录作用域、函数作用域影响,不会自动持久化。

缓存变量会写入 CMakeCache.txt,用户可以通过命令行、CMake GUI、ccmake 修改。

cmake 复制代码
set(ENABLE_TESTS ON CACHE BOOL "Enable tests")

命令行设置:

bash 复制代码
cmake -S . -B build -DENABLE_TESTS=ON

应用:

cmake 复制代码
option(BUILD_TESTING "Build tests" ON)

读取缓存变量:

cmake 复制代码
message("${ENABLE_TESTS}")
message("$CACHE{ENABLE_TESTS}")

一般来说,${VAR}会先查普通变量,再查缓存变量而$CACHE{VAR}会直接读取 cache entry

2. 参数变量

参数变量一般是指宏和函数的参数控制相关变量:

cmake 复制代码
macro(demo_macro arg1 arg2)
  message("ARG1: ${arg1}")
  message("ARG2: ${arg2}")
  message("ARGN: ${ARGN}")
endmacro()

应用:

cmake 复制代码
demo_macro(v1 v2 ext1 ext2 ext3)

它们有点类似于c/c++中main函数的传参的处理。主要有:

text 复制代码
ARGC:实参数量
ARGV:所有实参
ARGV0:第 1 个实参
ARGV1:第 2 个实参
ARGV2:第 3 个实参
ARGN:未被命名形参接收的剩余实参

3. 环境变量

CMake 中访问环境变量:

cmake 复制代码
message("$ENV{PATH}")

判断环境变量是否存在:

cmake 复制代码
if(DEFINED ENV{CC})
    message("CC=$ENV{CC}")
endif()

设置当前CMake进程内环境变量:

cmake 复制代码
set(ENV{MY_ENV} "abc")

它不会永久修改系统环境变量,也不会自动影响外部 shell,切记。

下面列出几个常见环境变量:

text 复制代码
CC
CXX
CFLAGS
CXXFLAGS
LDFLAGS
CMAKE_GENERATOR
CMAKE_BUILD_PARALLEL_LEVEL
CMAKE_PREFIX_PATH
CMAKE_TOOLCHAIN_FILE

4. 命令行变量

通过 -D(这个有点宏变量的味道)传入的变量通常进入 cache:

bash 复制代码
cmake -S . -B build \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_INSTALL_PREFIX=/opt/myapp \
  -DBUILD_SHARED_LIBS=ON

在掌握了大致的分类和初步的用法后,下面将对其中的一些常见的变量进行使用说明。

二、路径相关变量

1. CMAKE_SOURCE_DIR

顶层源码目录。

cmake 复制代码
message("Top source dir: ${CMAKE_SOURCE_DIR}")

无论在顶层还是子目录中,CMAKE_SOURCE_DIR 都指向最顶层源码目录。

2. CMAKE_BINARY_DIR

顶层构建目录。

cmake 复制代码
message("Top build dir: ${CMAKE_BINARY_DIR}")

例如:

bash 复制代码
cmake -S . -B build

则:

text 复制代码
CMAKE_BINARY_DIR = /path/to/project/build

3. CMAKE_CURRENT_SOURCE_DIR

当前正在处理的 CMakeLists.txt 所在源码目录。

cmake 复制代码
message("Current source dir: ${CMAKE_CURRENT_SOURCE_DIR}")

4. CMAKE_CURRENT_BINARY_DIR

当前目录对应的构建目录。

cmake 复制代码
message("Current binary dir: ${CMAKE_CURRENT_BINARY_DIR}")

5. PROJECT_SOURCE_DIR

当前 project对应的源码根目录。

cmake 复制代码
project(MyApp)
message("${PROJECT_SOURCE_DIR}")

6. PROJECT_BINARY_DIR

当前project对应的构建目录。

三、项目信息变量

1. PROJECT_NAME

当前项目名称。

cmake 复制代码
project(IvusSystem)
message("${PROJECT_NAME}")

2. CMAKE_PROJECT_NAME

顶层项目名称。如果子项目里也调用了project,PROJECT_NAME会变化,但 CMAKE_PROJECT_NAME仍是顶层项目名。

3. PROJECT_VERSION

项目版本。

cmake 复制代码
project(MyApp VERSION 1.2.3)
message("${PROJECT_VERSION}")

相关变量:

text 复制代码
PROJECT_VERSION_MAJOR
PROJECT_VERSION_MINOR
PROJECT_VERSION_PATCH
PROJECT_VERSION_TWEAK

应用:

cmake 复制代码
project(MyApp VERSION 1.2.3)
message("${PROJECT_VERSION_MAJOR}") # 1
message("${PROJECT_VERSION_MINOR}") # 2
message("${PROJECT_VERSION_PATCH}") # 3

四、构建类型变量

1. CMAKE_BUILD_TYPE

单配置生成器使用,例如 Ninja、Unix Makefiles。

bash 复制代码
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release

常见值:

text 复制代码
Debug
Release
RelWithDebInfo
MinSizeRel

需要说明的是,在跨平台如Visual Studio、Xcode、Ninja Multi-Config 这类多配置生成器通常不用它。

2. CMAKE_CONFIGURATION_TYPES

多配置生成器使用:

cmake 复制代码
message("${CMAKE_CONFIGURATION_TYPES}")

常见值:

text 复制代码
Debug;Release;RelWithDebInfo;MinSizeRel

3. CMAKE_DEBUG_POSTFIX

给 Debug 版本库名加后缀:

cmake 复制代码
set(CMAKE_DEBUG_POSTFIX "d")

可能生成:

text 复制代码
mylibd.dll
mylibd.lib

五、编译器相关变量

1. CMAKE_C_COMPILER

C编译器路径:

cmake 复制代码
message("C compiler: ${CMAKE_C_COMPILER}")

2. CMAKE_CXX_COMPILER

C++ 编译器路径:

cmake 复制代码
message("CXX compiler: ${CMAKE_CXX_COMPILER}")

3. CMAKE__COMPILER

通用的编译形式:

text 复制代码
CMAKE_C_COMPILER
CMAKE_CXX_COMPILER
CMAKE_CUDA_COMPILER
CMAKE_Fortran_COMPILER
CMAKE_OBJC_COMPILER
CMAKE_OBJCXX_COMPILER

4. CMAKE__COMPILER_VERSION

编译器版本:

cmake 复制代码
message("${CMAKE_CXX_COMPILER_VERSION}")

5. CMAKE__COMPILER_TARGET

交叉编译目标 :

cmake 复制代码
set(CMAKE_CXX_COMPILER_TARGET aarch64-linux-gnu)

六、编译选项变量

1. CMAKE_C_FLAGS

全局 C编译选项:

cmake 复制代码
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")

2. CMAKE_CXX_FLAGS

全局 C++ 编译选项:

cmake 复制代码
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")

3. 配置相关编译选项

推荐使用:

cmake 复制代码
target_compile_options(myapp PRIVATE -Wall -Wextra)

而不是全局修改 CMAKE_CXX_FLAGS

七、C++ 标准相关变量

1. CMAKE_CXX_STANDARD

设置默认 C++ 标准。

cmake 复制代码
set(CMAKE_CXX_STANDARD 17)

2. CMAKE_CXX_STANDARD_REQUIRED

要求必须满足该标准。

cmake 复制代码
set(CMAKE_CXX_STANDARD_REQUIRED ON)

3. CMAKE_CXX_EXTENSIONS

是否启用编译器扩展,推荐target级别写法:

cmake 复制代码
target_compile_features(myapp PRIVATE cxx_std_17)

八、链接相关变量

1. CMAKE_EXE_LINKER_FLAGS

可执行文件链接选项。

cmake 复制代码
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed")

2. 其他链接变量

text 复制代码
CMAKE_SHARED_LINKER_FLAGS
CMAKE_MODULE_LINKER_FLAGS
CMAKE_STATIC_LINKER_FLAGS

推荐使用:

cmake 复制代码
target_link_options(myapp PRIVATE -Wl,--as-needed)
target_link_libraries(myapp PRIVATE pthread)

九、输出目录变量

1. CMAKE_RUNTIME_OUTPUT_DIRECTORY

可执行文件输出目录:

cmake 复制代码
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

2. CMAKE_LIBRARY_OUTPUT_DIRECTORY

动态库输出目录:

cmake 复制代码
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

3. CMAKE_ARCHIVE_OUTPUT_DIRECTORY

静态库输出目录。

cmake 复制代码
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

也可以按配置区分:

cmake 复制代码
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin/debug)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin/release)

推荐使用:

cmake 复制代码
set_target_properties(myapp PROPERTIES
    RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
)

十、查找包和查找路径变量

1. CMAKE_PREFIX_PATH

find_package()、find_library()、find_path()查找依赖的路径:

bash 复制代码
cmake -S . -B build -DCMAKE_PREFIX_PATH=/opt/Qt/6.5.3/gcc_64

也可以使用环境变量:

bash 复制代码
export CMAKE_PREFIX_PATH=/opt/Qt/6.5.3/gcc_64

2. CMAKE_MODULE_PATH

CMake自定义 FindXXX.cmake的路径:

cmake 复制代码
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
find_package(MyLib REQUIRED)

3. CMAKE_FIND_ROOT_PATH

交叉编译时的根路径:

cmake 复制代码
set(CMAKE_FIND_ROOT_PATH /opt/sysroot)

十一、目标构建行为变量

1. BUILD_SHARED_LIBS

add_library()默认生成静态库或动态库。

cmake 复制代码
set(BUILD_SHARED_LIBS ON)
add_library(mylib a.cpp)

如果 BUILD_SHARED_LIBS=ON,默认生成动态库。

如果显式写了 STATIC或SHARED,它不生效:

cmake 复制代码
add_library(mylib STATIC a.cpp)

2. CMAKE_VERBOSE_MAKEFILE

Makefile构建时显示详细命令:

cmake 复制代码
set(CMAKE_VERBOSE_MAKEFILE ON)

或者:

bash 复制代码
cmake --build build --verbose

十二、函数和宏参数变量

在前面介绍了function或 macro中,有一组特殊参数变量,这些变量只在 function/ macro调用上下文中有意义,常用于实现可变参数函数。

1. ${ARGV} 和 ARGV0、{ARGV0}、ARGV0、{ARGV1}

${ARGV} 表示调用时传入的所有实参。${ARGV0}${ARGV1}${ARGV2} 等表示按下标访问单个实参。

示例:

cmake 复制代码
function(demo a b)
    message("a = ${a}")
    message("b = ${b}")

    message("ARGC = ${ARGC}")
    message("ARGV = ${ARGV}")
    message("ARGV0 = ${ARGV0}")
    message("ARGV1 = ${ARGV1}")
    message("ARGV2 = ${ARGV2}")
    message("ARGV3 = ${ARGV3}")
    message("ARGN = ${ARGN}")
endfunction()

demo(one two three four)

输出含义:

text 复制代码
a = one
b = two
ARGC = 4
ARGV = one;two;three;four
ARGV0 = one
ARGV1 = two
ARGV2 = three
ARGV3 = four
ARGN = three;four

对应关系:

text 复制代码
demo(one two three four)
     │   │   │     │
     │   │   │     └── ARGV3 = four
     │   │   └──────── ARGV2 = three
     │   └──────────── ARGV1 = two
     └──────────────── ARGV0 = one

其中 ab 是命名形参:

cmake 复制代码
function(demo a b)

所以:

text 复制代码
a = ARGV0
b = ARGV1
ARGN = 从 ARGV2 开始剩下的参数

2. ${ARGN}

${ARGN} 表示:

text 复制代码
调用 function / macro 时,没有被显式命名形参接收的剩余参数列表

示例:

cmake 复制代码
function(print_args first second)
    message("first = ${first}")
    message("second = ${second}")
    message("ARGN = ${ARGN}")
endfunction()

print_args(A B C D E)

输出含义:

text 复制代码
first = A
second = B
ARGN = C;D;E

也就是说:

text 复制代码
first 接收 A
second 接收 B
ARGN 接收剩下的 C D E

3. ${ARGC}

${ARGC} 表示实参数量:

cmake 复制代码
function(count_args)
    message("argc = ${ARGC}")
endfunction()

count_args(a b c)

输出:

text 复制代码
argc = 3

4. 封装可变参数命令

例如封装 add_executable()

cmake 复制代码
function(my_add_executable target)
    message("target = ${ARGV0}")
    message("sources = ${ARGN}")

    add_executable(${target} ${ARGN})
endfunction()

my_add_executable(app main.cpp util.cpp net.cpp)

含义:

text 复制代码
ARGV0 = app
target = app
ARGN = main.cpp;util.cpp;net.cpp

实际等价于:

cmake 复制代码
add_executable(app main.cpp util.cpp net.cpp)

5. 列表特性

${ARGV}${ARGN} 都是 CMake 列表,多个参数之间用分号 ; 分隔:

cmake 复制代码
function(show_all)
    foreach(item IN LISTS ARGV)
        message("item = ${item}")
    endforeach()
endfunction()

show_all(a b c)

输出:

text 复制代码
item = a
item = b
item = c

总结:

text 复制代码
ARGV 是所有参数
ARGV0 / ARGV1 / ARGV2 是按下标访问单个参数
ARGC 是参数个数
ARGN 是未被命名形参接收的剩余参数

十三、推荐用法

1. 项目选项用 option()

cmake 复制代码
option(ENABLE_LOG "Enable log" ON)

不要写成普通变量,否则用户不方便通过 -D 配置。

2. 建议尽可能不使用全局 CMAKE_CXX_FLAGS

不建议:

cmake 复制代码
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")

建议:

cmake 复制代码
target_compile_options(myapp PRIVATE -Wall -Wextra)

十四、总结

对于常见的CMake变量还是要准确把握,用到细节一些不太常见的如一些策略变量或具体到某个平台的变量再查看相关的文档即可。开发者是要便利的使用CMake作为工具,而不要将其作为一种应用上负担,抓住重点,切记。

相关推荐
liu-yonggang1 小时前
MISRA C++:2008 — Guidelines for the use of the C++ language in critical systems
c++
凡人叶枫1 小时前
Effective C++ 条款05:了解 C++ 默默编写并调用哪些函数
java·linux·开发语言·c++·effective c++·编程范式
少司府1 小时前
C++进阶:AVL树
开发语言·数据结构·c++·二叉树·avl树
挨代码1 小时前
UEC++ 数据类型及继承关系
c++·ue
代码中介商2 小时前
C++11右值引用与移动语义深度解析
开发语言·c++
码上有光2 小时前
c++:二叉搜索树(map和set的底层结构)
开发语言·c++·递归·二叉搜索树
Brilliantwxx2 小时前
【C++】 链式哈希表(Separate Chaining)
c++·哈希算法·散列表
大白话_NOI2 小时前
【洛谷 P1480】A/B Problem(高精度除法 Ⅰ)详细题解
c++
j7~2 小时前
【C++】C&C++内存管理--之内存分布,operatenew/new,operate/delete的底层原理.
c语言·c++·delete·内存泄漏·new·operate new·动态内存分布