cmake-变量作用域

本文的目标是讲讲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() and unset() 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语言的变量名大小写敏感)
图1

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. 这一句话包含的信息比较多,需细品。

我把这个作用域叫做函数作用域。当在函数内通过setunset将``变量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}")

图2

函数外部定义的变量可以在函数内部被引用。

函数内部定义的变量在函数外部是否可以引用?

# 函数内定义的变量能否在函数外部被引用
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}")

图3

函数内部定义的变量仅在函数内部有效。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()

图4

内层嵌套函数会依次向外搜索指定被引用的变量。

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 with cmake -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变量的值。

  • 执行结果
    图6

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 the set() and unset() 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变量,也能获取到其值。

  • 12
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sif_666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值