Bootstrap

【02】CMake中变量的设置

CMake中变量的设置

1. 前言

从源码编译 OpenCV 时都会用到 CMake 的内容,并且还经常通过 CMake 来为 OpenCV 项目进行配置。配置的过程中,如何向用户展示必要的参数以及屏蔽一些局部使用的参数显得十分重要。
变量的属性与 C/C++ 语言类似,也有局部变量、全局变量的说法,此处的全局变量最正确的说法应该是缓存变量,表示为记录在 CMakeCache.txt 文件中的变量。通常这些变量是能够被用户自定义修改的,例如在安装 OpenCV 的时候,使用 cmake-gui 的时候就会看到。一般通过修改这些变量,即可实现用户的自定义配置需求。

2. 局部变量

2.1 定义

一般使用以下命令即可定义一个局部变量

set(AA "aaa")      # AA 内容为一个值:"aaa"
set(BB "bb" "bbb") # BB 内容为两个值:"bb" 和 "bbb"
set(CC ${BB} "cc") # CC 内容为三个值:"bb"、"bbb" 和 "cc"
set(DD "${CC}")    # DD 内容为一个值:"${CC}",再展开则为:" "bb","bbb","cc" "

这种变量定义后在当前模块,及其子模块中能够访问,子模块的含义是:通过 include 命令找到的 *.cmake 文件或者 add_subdirectory 找到的 CMakeLists.txt 文件
如下是OpenCV中变量定义的内容:

# 定义于 <opencv-path>/CMakeLists.txt 中的内容
set(LIBRARY_OUTPUT_PATH "${OpenCV_BINARY_DIR}/lib")

LIBRARY_OUTPUT_PATH 则包含一个值:"${OpenCV_BINARY_DIR}/lib"

注意事项

值得注意的是,我们平常在使用以下语句时:

target_link_libraries(my_test ${OpenCV_LIBS})

${OpenCV_LIBS} 包含了众多变量,为一个列表,但如果使用 "${OpenCV_LIBS}" 这种写法,也只是一个变量,这两种写法对于foreach 语句以及宏、函数等内容,表现出的效果是不一样的。

2.2 解除

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

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

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

3. 缓存变量

缓存变量在定义后便被添加至了 CMakeCache.txt 文件中,之后访问它的时候,将从 CMakeCache.txt 文件中调出,因此均能得到有效访问数据。但是要注意定义和访问的先后顺序,如果在未定义时期访问了该数据,则会得到一个默认为空的结果。

3.1 自定义缓存变量

3.1.1 编译选项 option

使用 option 命令是创建 BOOL 型缓存变量(编译选项)的一种方式,使用方式如下:

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

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

3.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 效果相同,除此之外,还有以下几个常用的类型:BOOLPATH
之后跟的字符串则是这个变量的注释,在 cmake-gui 工具中,把鼠标放置在左侧的变量名上,即可显示出该变量的注释。要注意,缓存变量必须不经过 mark_as_advanced 设置为高级变量才能被 cmake-gui 直接展示,否则需要在 cmake-gui 界面中勾选显示高级变量。

同样,缓存变量的解除也是使用 unset 语句。但略有不同的是,需要加上 CACHE

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

3.2 内置环境变量

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

这种变量在定义时与局部变量的定义方式一致,但是其作用方式是缓存变量的方式,例如:

set(CMAKE_BUILD_TYPE Release)

4. 变量规范

4.1 设置规范

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

if(" ${ARGV1}" STREQUAL " INTERNAL" OR " ${ARGV1}" STREQUAL " BINDINGS")
    set(OPENCV_MODULE_${the_module}_CLASS "${ARGV1}" CACHE INTERNAL "The category of the module")
    set(__ocv_argn__ ${ADD_MODULE_ARGN})
    list(REMOVE_AT __ocv_argn__ 0)
    ocv_add_dependencies(${the_module} ${__ocv_argn__})
    unset(__ocv_argn__)
else()
    set(OPENCV_MODULE_${the_module}_CLASS "PUBLIC" CACHE INTERNAL "The category of the module")
    ocv_add_dependencies(${the_module} ${ADD_MODULE_ARGN})
    if(BUILD_${the_module})
        set(OPENCV_MODULES_PUBLIC ${OPENCV_MODULES_PUBLIC} "${the_module}" CACHE INTERNAL "List of OpenCV modules marked for export")
    endif()
endif()

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

4.2 命名规范

  1. 外部访问的库文件集合的命名:OpenCV_LIBS(统一 _LIBS 后缀)
  2. 外部访问的头文件集合的命名:OpenCV_INCLUDE_DIRS(统一 _INCLUDE_DIRS 后缀)
  3. 目标库 xxx 的命名:opencv_xxx(设置统一的前缀来跟其他文件进行区分)
;