本文的目标是讲讲cmake中的变量。在一个复杂的cmake工程中,变量经常会被传来传去,只有搞懂变量的原理和本质,才能以不变应万变,而不会出现"变量的值是从哪儿来的问题"。要想将变量讲清楚,涉及的新的知识点还比较多,看完本文一定有所收获。
变量(variables)
Variables are the basic unit of storage in the CMake Language. Their values are always of string type, though some commands may interpret the strings as values of other types. The
set()
andunset()
commands explicitly set or unset a variable, but other commands have semantics that modify variables as well. Variable names are case-sensitive and may consist of almost any text, but we recommend sticking to names consisting only of alphanumeric characters plus_
and-
.
变量是cmake语言的基本存储单元。变量名大小写敏感。通常变量名由字母、数字、_
和-
构成。
cmake_minimum_required(VERSION 3.12)
project(test09)
set(hello "hello1")
set(hEllo "hEllo2")
message("hello:${hello}")
message("hEllo:${hEllo}")
cmake后,输出以下打印信息:(说明cmake语言的变量名大小写敏感)
Variables have dynamic scope. Each variable “set” or “unset” creates a binding in the current scope
类似c语言变量的作用域,cmake变量也有作用域(scope),可以通过"set"或"unset"来绑定(binding)作用域。
Function Scope
Command Definitions created by the
function()
command create commands that, when invoked, process the recorded commands in a new variable binding scope. A variable “set” or “unset” binds in this scope and is visible for the current function and any nested calls within it, but not after the function returns.
A variable “set” or “unset” binds in this scope and is visible for the current function and any nested calls within it, but not after the function returns. 这一句话包含的信息比较多,需细品。
我把这个作用域叫做函数作用域。当在函数内通过set
或unset
将``变量v与当前函数作用域绑定时,
变量v`的新值仅在函数作用域内有效,出了这个作用域,如果这个作用域外也有同名的变量v,那么使用将是域外同名变量v的值;func1()内部调用func2(),嵌套调用的函数func2()内部如果也引用变量v,那么该变量v应该是func1()内部定义的变量,如果有的话,如果func1()内部没有绑定变量v,那么就会使用func1()外绑定的变量v,依次向外搜索。
接下来,通过实际的例子来进一步解读上面着一段话。重点!!!
函数外部定义的变量在函数内部是否可以引用?
# 函数内部可以引用函数外部的变量
set(hello "hello1")
function(foo)
message("函数内部引用外部变量:${hello}")
endfunction()
foo()
message("hello:${hello}")
函数外部定义的变量可以在函数内部被引用。
函数内部定义的变量在函数外部是否可以引用?
# 函数内定义的变量能否在函数外部被引用
set(hello "hello1")
function(foo)
message("函数内部引用外部变量,hello:${hello}")
set(hello "hello2") # 在函数内部set了一个与函数外部变量同名的内部变量, 该内部变量的作用域仅在函数内部,函数外部仍然使用的是外部变量
set(world "world")
message("函数内部变量,hello:${hello}, world:${world}")
endfunction()
foo()
message("hello:${hello}")
message("world:${world}")
函数内部定义的变量仅在函数内部有效。set命令将变量与当前作用域绑定。例如,CMakeLists.txt的第5、6行,就是将变量hello和world绑定到当前函数作用域。
外层函数定义的变量,是否能被内层函数引用?
# 函数嵌套调用, 外层函数定义的变量能否被内层函数引用
set(hello "hello1")
set(hellox "hellox")
function(inter_func)
message("内层函数引用(外部变量)或(外层函数的变量),hello:${hello}")
message("内层函数引用(外部变量)或(外层函数的变量),hellox:${hellox}")
endfunction()
function(foo)
message("外部变量,hello:${hello}")
set(hello "hello2")
message("外层函数的内部变量,hello:${hello}")
inter_func() # 嵌套调用
endfunction()
foo()
message("hello:${hello}")
inter_func()
内层嵌套函数会依次向外搜索指定被引用的变量。
Directory Scope
Each of the Directories in a source tree has its own variable bindings. Before processing the
CMakeLists.txt
file for a directory, CMake copies all variable bindings currently defined in the parent directory, if any, to initialize the new directory scope. CMake Scripts, when processed withcmake -P
, bind variables in one “directory” scope.A variable “set” or “unset” not inside a function call binds to the current directory scope.
我把这个作用域叫做目录作用域。子目录的CMakeLists.txt会将父目录的所有变量拷贝到当前CMakeLists.txt中,当前CMakeLists.txt中的变量的作用域仅在当前子目录有效。
目录作用域有两个特点:向下有效,值拷贝。举个栗子来进一步阐述
- 目录结构
|-- dir # 父目录
| |-- CMakeLists.txt
| `-- subdir # 子目录
| `-- CMakeLists.txt
- 父目录CMakeLists.txt
set(parent_var1 "parent_var1")
message("dir-parent_var1:${parent_var1}")
add_subdirectory(subdir)
message("dir-parent_var1:${parent_var1}")
在父目录中绑定了一个变量parent_var1,它的值为"parent_var1"。
- 子目录CMakeLists.txt
set(subdir_var1 "subdir_var1")
message("subdir-subdir_var1:${subdir_var1}")
message("subdir-parent_var1:${parent_var1}")
set(parent_var1 "修改后的parent_var1")
message("subdir-parent_var1:${parent_var1}")
在子目录中,第1行绑定了一个变量subdir_var1,它的值为"subdir_var1";第3行打印parent_var1变量,由于变量向下有效,值拷贝的特点,子目录的CMakeLists.txt执行前,先将父目录的变量parent_var1拷贝到CMakeLists.txt(的执行环境)中,所以会打印subdir-parent_var1:parent_var1;第4行修改parent_var1变量的值,将其值改为"修改后的parent_var1";第5行打印输出subdir-parent_var1:修改后的parent_var1;但是父目录CMakeLists.txt的第4行打印输出仍为"dir-parent_var1:parent_var1",说明子目录的parent_var1的作用域仅在子目录范围内,不会影响到父目录的parent_var1变量的值。
- 执行结果
Persistent Cache
CMake stores a separate set of “cache” variables, or “cache entries”, whose values persist across multiple runs within a project build tree. Cache entries have an isolated binding scope modified only by explicit request, such as by the
CACHE
option of theset()
andunset()
commands.
缓存变量在整个cmake工程的编译生命周期内都有效,工程内的其他任意目录都可以访问缓存变量,注意cmake是从上到下来解析CMakeLists.txt文件的。
- 目录结构
|-- CMakeLists.txt # top CMakeLists.txt
|-- func
| `-- CMakeLists.txt # func CMakeLists.txt
|-- persistent
| `-- CMakeLists.txt # persistent CMakeLists.txt
- top CMakeLists.txt
cmake_minimum_required(VERSION 3.12)
project(test09)
add_subdirectory(persistent)
add_subdirectory(func)
- func CMakeLists.txt
# 函数嵌套调用, 外层函数定义的变量能否被内层函数引用
set(hello "hello1")
set(hellox "hellox")
function(inter_func)
message("内层函数引用(外部变量)或(外层函数的变量),hello:${hello}")
message("内层函数引用(外部变量)或(外层函数的变量),hellox:${hellox}")
endfunction()
function(foo)
message("外部变量,hello:${hello}")
set(hello "hello2")
message("外层函数的内部变量,hello:${hello}")
inter_func() # 嵌套调用
endfunction()
foo()
message("hello:${hello}")
inter_func()
message("func-${cache_var1}") # 主要看这一行
- persistent CMakeLists.txt
set(cache_var1 "persistent-缓存变量1" CACHE STRING "abc")
message("${cache_var1}")
在func目录下的CMakeLists.txt中引用cache_var1变量,也能获取到其值。