【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
注意
- 即使
bbb
不加""
号,也表示一个字符串,因为 ,但不妨碍某些命令可以将其解释为其他类型; - 建议变量的内容要么都使用引号
""
,要么都不使用,例如val2
的写法就不好 - 变量内容可以通过空格
或者分号
;
进行分隔,也可以混用,例如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
会提供一个复选框;FILEPATH
:磁盘上文件的路径。cmake-gui
会提供一个文件对话框;PATH
:磁盘上目录的路径。cmake-gui
会提供一个文件对话框;STRING
:一行文字。cmake-gui
会提供文本字段,如果设置了STRINGS
属性也可以提供下拉选项;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 命名规范
- 普通变量可以使用小写 + 下划线的方式,例如
set(my_var "ABC")
- 缓存变量使用大写 + 下划线的方式,例如
set(MY_VAR "ABC" CACHE STRING "My variable")