华南虎视觉组主页华南虎视觉组主页
首页
项目
通用
教程
Gitea
首页
项目
通用
教程
Gitea
  • 现代 CMake 教程

    • 【01】安装与基本介绍
    • 【02】CMake 的文件分布
    • 【03】变量的设置与引用
      • 1. 前言
      • 2. 普通变量
        • 2.1 定义
        • 2.2 引用
        • 2.2 解除
        • 2.3 作用域提升
      • 3. CMake 变量
        • 3.1 种类
        • 3.2 定义
      • 4. 缓存变量
        • 4.1 定义
        • 4.2 引用
        • 4.3 解除
      • 5. 环境变量
      • 6. 变量规范
        • 6.1 设置规范
        • 6.2 命名规范
    • 【04】运算符与条件、循环语句
    • 【05】目标构建
    • 【06】变量参与 C++ 程序的编译
    • 【07】宏与函数
    • 【08】find_package 详解
    • 【09】生成器表达式
    • 【10】项目的导出与安装
    • 【11】项目实战
    • 【12】CTest
    • 【13】CPack

【03】变量的设置与引用

1. 前言

从源码编译 OpenCV 时都会用到 CMake 中的变量,并且还经常通过这些变量来为 OpenCV 项目的构建、安装过程进行配置。配置的过程中,如何向用户展示必要的参数以及屏蔽一些局部使用的参数显得十分重要。

C++ 中的变量具备自动存储期、静态存储期、线程存储期以及动态存储期,CMake 也具有类似的效果,变量在划分上具有

  • 普通变量
  • 缓存变量
  • 环境变量

3 种,此内容的详细描述可参考变量。

2. 普通变量

此类变量具有与C/C++语言中的自动存储期类似的效果,一般情况下,

  • 在函数function中创建的变量不会在函数以外生效或被访问,可类比 C/C++ 的函数;
  • CMakeLists.txt可以继承父目录文件中定义的变量,在处理CMakeLists.txt文件之前,CMake 会复制父目录中定义的所有变量(如果有),以初始化当前作用域。

2.1 定义

一般使用以下命令即可定义一个普通变量

set(val1 "aaa")    # val1 内容为 1 个值:aaa
set(val2 "bb" bbb) # val2 内容为 2 个值:bb;bbb
set(val3 aa;bb)    # val3 内容为 2 个值:aa;bb
set(val4 aa;bb cc) # val4 内容为 3 个值:aa;bb;cc

注意

  1. 即使 bbb 不加 "" 号,也表示一个字符串,因为 ,但不妨碍某些命令可以将其解释为其他类型;
  2. 建议变量的内容要么都使用引号 "",要么都不使用,例如 val2 的写法就不好
  3. 变量内容可以通过空格 或者分号 ; 进行分隔,也可以混用,例如 val4

2.2 引用

可以理解为取值操作,基本的形式为${xxx}

set(AA xy mn)
set(BB ${AA} cc) # BB 内容为 3 个值:xy;mn;cc
set(CC "${BB}")  # CC 内容为 3 个值:xy;mn;cc

2.2 解除

unset()语句则是解除定义的功能,例如:

# 定义于 <opencv-path>/CMakeLists.txt 中的内容
unset(_hal_includes)

它可以将变量的内容清除,下次再次使用同名的变量需要重新 set

2.3 作用域提升

一般情况下,子目录的变量无法在父目录 CMakeLists.txt 中生效,但是父目录的 CMakeLists.txt 中定义好的变量可以被子目录的 CMakeLists.txt 所继承,但开辟了新的作用域。

如果我们希望在子目录的 CMakeLists.txt 中设置变量,并且能够在父目录 CMakeLists.txt 中生效,我们需要提升作用域,语法是

set(xxx PARENT_SCOPE)

例如,项目根目录下的 CMakeLists.txt 中写入了以下内容

message(STATUS "var1 = ${var1}")
message(STATUS "var2 = ${var2}")
add_subdirectory(module)
message(STATUS "var1 = ${var1}")
message(STATUS "var2 = ${var2}")

而在 module 模块的文件对应的 CMakeLists.txt 中写入

set(var1 "abc")
set(var2 "def" PARENT_SCOPE)

在执行 CMake 后,运行结果为

-- var1 =
-- var2 =
-- var1 =
-- var2 = def

在使用 PARENT_SCOPE 后,var2 变量在父目录 CMakeLists.txt 中也能生效。

3. CMake 变量

3.1 种类

此为 CMake 默认提供的变量,也将记载在 CMakeCache.txt 中。具体包括

  • 提供信息的变量,例如 CMAKE_BINARY_DIR、PROJECT_NAME
  • 改变行为的变量,例如 CMAKE_BUILD_TYPE
  • 描述系统的变量,例如 CMAKE_SYSTEM_PROCESSOR
  • 控制构建的变量,例如 LIBRARY_OUTPUT_PATH、CMAKE_RUNTIME_OUTPUT_DIRECTORY
  • 语言的变量,例如 CMAKE_CXX_STANDARD
  • CTest 与 CPack 的变量

3.2 定义

这种变量在定义时与普通变量的定义方式一致,但效果上是缓存变量,例如:

set(CMAKE_BUILD_TYPE Debug)

在底层 CMakeLists.txt 文件中被设置,但能够在之后访问到的其他 CMakeLists.txt 中。

4. 缓存变量

这种变量在定义后便被添加至了 CMakeCache.txt 文件中,之后访问它的时候,将从 CMakeCache.txt 文件中调出,因此均能得到有效访问数据。

提示

这类变量可以用 C/C++ 具有动态存储期的变量来类比

4.1 定义

上文提到 CMake 变量都是字符串类型的,但不妨碍解释成其他类型,在缓存变量中,可以解释成其他类型可供 cmake-gui 等工具显示。

4.1.1 option

其中,使用 option 命令是创建 BOOL 型缓存变量的一种方式,一般可以作为编译选项,即使官网中没有在缓存变量中介绍该命令。option 的使用方式如下:

option(ORT_WITH_CUDA "the library compile with CUDA" OFF)

它只有两种状态,TRUE、FALSE 或用另一种表示方式:ON、OFF,最常见的 option 变量就是在使用 cmake-gui 工具的时候,右侧栏中可以打勾的方框。

4.1.2 自定义缓存变量

即使用命令 set(xxx CACHE),这是一般的设置缓存变量的方式,可以参考 OpenCV 中的例子:

# 定义于 <opencv-path>/cmake/OpenCVModule.cmake 中的内容
set(OPENCV_MODULES_BUILD "" CACHE INTERNAL "List of OpenCV modules included into the build")

CACHE 指明了这是一个缓存变量,INTERNAL 的效果与 STRING 效果大致相同,但 INTERNAL 无法在 CMake 工具中显示,用户无法通过 cmake-gui 等工具进行修改,但 STRING 可以。除此之外,还有以下几个常用的类型:BOOL、PATH、FILEPATH。其中BOOL型缓存变量与使用 option 创建的变量效果等同。

之后跟的字符串则是这个变量的注释,在 cmake-gui 工具中,把鼠标放置在左侧的变量名上,即可显示出该变量的注释。

4.1.3 官网介绍

  • BOOL:ON/OFF 值。cmake-gui 会提供一个复选框;

    20240213-003326

  • FILEPATH:磁盘上文件的路径。cmake-gui 会提供一个文件对话框;

    20240213-003348

  • PATH:磁盘上目录的路径。cmake-gui 会提供一个文件对话框;

  • STRING:一行文字。cmake-gui 会提供文本字段,如果设置了 STRINGS 属性也可以提供下拉选项;

    20240213-003339

  • INTERNAL:一行文字。cmake-gui 会不显示该类型的变量,因此在使用此类型时意味着 FORCE

4.2 引用

与普通变量的引用方式一致,即 ${xxx},但 ${xxx} 会。若强制指定搜索缓存变量,则需要使用 $CACHE{xxx},例如

set(val1 "aaa")
set(val2 "bbb" CACHE STRING "Test String")

message(STATUS ${val1})      # -- aaa
message(STATUS ${val2})      # -- bbb
message(STATUS $CACHE{val1}) # --
message(STATUS $CACHE{val2}) # -- bbb

4.3 解除

同样也是使用unset语句。但略有不同的是,需要加上CACHE:

# 定义于 <opencv-path>/cmake/OpenCVModule.cmake 中的内容
unset(OPENCV_WORLD_MODULES CACHE)

5. 环境变量

与系统中的环境变量是一个概念,比如 LD_LIBRARY_PATH,要引用这种变量我们需要使用 $ENV{xxx},例如

set(sdk_path "$ENV{MVCAM_SDK_PATH}")
if(sdk_path STREQUAL "")
  return()
endif()

其中 MVCAM_SDK_PATH 是系统环境中的一个变量(定义在 .bashrc 里面)

6. 变量规范

6.1 设置规范

来看 OpenCV 中宏 ocv_add_module 内部的一个条件控制语句的写法:

if(...)
  # ...
  set(__ocv_argn__ ${ADD_MODULE_ARGN})
  # ...
  unset(__ocv_argn__)
  # ...
endif()

可以看到,${ADD_MODULE_ARGN} 在整个条件运算中均未定义,因此他对于这个语句来说是个外部变量。对于外部变量,OpenCV的做法通常与上面的代码一致,即 set() 一个新的变量来记录这个可能会做修改的值,并且在这个变量用完后 unset()。

6.2 命名规范

  1. 普通变量可以使用小写 + 下划线的方式,例如
    set(my_var "ABC")
    
  2. 缓存变量使用大写 + 下划线的方式,例如
    set(MY_VAR "ABC" CACHE STRING "My variable")
    
Prev
【02】CMake 的文件分布
Next
【04】运算符与条件、循环语句