目录
cmake普通变量
如何取消变量
cmake环境变量
cmake缓存变量
普通变量使用:
缓存变量使用:
cmake变量的作用域
block()
block demo:
function 函数作用域简单 demo
高级变量
总结:
和其他语言一样,cmake完全可以看做是一种编程语言,他有变量,有函数等.
cmake普通变量
cmake定义变量的命令如下:
set(<variable> <value>... [PARENT_SCOPE])
- cmake变量的约定:
- cmake当所有的变量的值是为字符串
- 当给出变量的值不包括空格的时候,可以不使用双引号,但是都建议加上双引号,不然一些奇怪的问题很难调试.
- cmake使用空格或者分号作为字符串的分隔符
- cmake中想要获取变量的值和shell脚本一样,采用${var}的形式
- 使用cmake变量前不需要这个变量已经定义了,如果使用了未定义的变量,那么它的值就是一个空的字符串.
- 默认情况下使用未定义的字符串不会有警告信息,但是可以通过cmake的-warn-uninitialzed的选项启用警告信息.
- 使用未定义的变量非常常见,如果出现问题也不一定是因为变量未定义导致的,所以-warn-uninitialzed选项用处很有限.
- 变量的值可以包含换行,也可以包含引号,不过需要转义.
第一个参数是必须的,代表要定义的变量的变量名.
第二个参数也是必须的,代表要定义的变量的值,根据上述cmake变量的约定,,我们知道这里的value应该是一个字符串,而且不管有没有空格,都建议用双引号引起来.
当然,第二个参数可以是一个以空格或者分号隔开的字符串,这样定义的变量将是一个列表.
第三个参数是个可选参数,意思是定义这个变量的作用域属于父作用域.
例如:
1.set(src "*.cpp")
2.set(src main.cpp a.cpp)
3.set(CPR_BUILD_CMD [[
#!/bin/bash
cmake -S . -B build
cmake --build build
)
4.set(shellScript [=[
~!/bin/bash
[[ -n "${USER}" ]] && echo "Have USER"
]=]
)
1,2比较简单就不说了,
3表示换行的,跟lua语言相似,使用[[开头 使用]]结尾
4表示换行中也有换行,使用[=[这个中间可以使用任意字符这里使用=了,结束的时候时候必须使用]=],因为要跟前面对饮起来.
如何取消变量
set(asd)
unset(asd)
unset就取消了这个变量.
cmake环境变量
不管是Windows,Linux还是macos都有环境变量的概念.我们经常使用的环境变量要数PATH这个环境变量了.Windows上可以在高级环境设置->环境变量中看到,Linux和macos上可以使用echo $PATH 或者export这条命令看到环境变量PATH的值.
除了PATH这个环境变量,对于日常开发中,我们通常还会有自定义的环境变量.比如我们安装了一个第三方软件,然后需要将一些安装目录导出到环境变量,这个时候就可能会用到自定义环境变量.
有些环境变量,只有我们自己的项目需要,所以就没必要为整个系统配置这类环境变量.cmake为我们提供了定义环境变量的方式,这样就可以让cmake定义的环境变量只在当前运行的cmake进程中生效,不会影响到系统或者其他进程的环境变量.
cmake定义的环境变量和获取环境变量的值的普通变量类似,只需要多加一个ENV标识符.
set(ENV{PATH} "/opt/myDir") # 定义系统环境变量,只在cmake进程中有
set(ENV{PATH} "/opt/myDir:ENV{PATH}") #定义只在cmake进程使用的环境变量和系统环境一起打印
message(STATUS "PATH=$ENV{PATH}") #打印字符
设置环境变量和使用环境变量在简单的cmake项目中很少见,大型跨平台项目中就比较常见.
系统环境变量只在配置阶段有效,在编译阶段就找不到了为空了.
cmake缓存变量
与普通变量不同的是,缓存变量的值是可以缓存到CMakeLists.txt文件中, 当再次运行cmake时,可以从中获取上一次的值,而不是重新去评估.所以缓存变量的作用域时全局的.
cmake定义缓存变量的格式如下:
set(varName value... CACHE type "docstring" [FORCE])
和普通变量比起来,缓存变量携带了更多的信息.缓存变量有了类型了,而且可以为缓存变量添加一个说明信息.
从上面的cmake定义缓存变量的命令中,我们可以得到,第一个参数依然是变量名字,第二个参数是变量的值.缓存变量不同于普通变量是从第三个参数开始的.第三个参数是固定CACHE这个关键字,表示这条命令定义的是缓存变量.
第四个变量type是必选参数,而且其值必须是下列值之一.
- BOOL
- BOOL类型的变量值如果是ON,TRUE,1则被评估为真,如果是OFF,FALSE,0则被评估为假.
- 当然除了上面列出来的值还有其他的值,但是判断真假就没有那么清晰了,所以建议定义BOOL类型的缓存变量的时候其值就采用上述列出的值,虽然不区分大小写,但是建议统一使用大写.
- FILEPATH
- 文件路径
- STRING
- 字符串
- INTERNAL
- 内部缓存变量不会对用户可见,一般是项目为了缓存某种内部信息时才使用的,cmake图形化界面工具也对其不可见.
- 内部缓存变量默认时FORCE的
- FORCE关键字代表每次运行都强制更新缓存变量的值,如果没有该关键字,当再次运行cmake的时候,cmake将使用CMakeCache.txt文件中缓存的值,而不是重新进行评估.
cmake自身是将所有的值均视为字符串的,这里指定类型只是为了提高cmake图形界面工具的用户体验.
第五个参数是一个说明性的字符串,可以为空,只在图形化cmake界面会展示.由于BOOL类型的变量使用频率非常高,cmake为其单独提供了一条命令.
option(optVal helpString [initialValue])
第一个参数是变量名字,第二个参数是提供帮助信息的字符串,initialValue是可选参数,代表缓存变量的值,如果没有提供,那该缓存变量的默认值认为是OFF.
上述命令等价于:
set(optVal initialValue CACHE BOOL helpString)
不过上述两个命令定义缓存变量是有一点点区别的,option()命令没有FORCE关键字.
更改缓存变量cmake -D的形式去更改.
普通变量使用:
那cmake什么时候该使用不同变量了?什么时候该使用缓存变量了?
普通变量适用于变量的值相对固定,而且只在某一个很小的作用域生效的场景.
缓存变量使用:
缓存变量适用于其可以随时更改,作用域作为全局的情况.经常在cmake中定义缓存变量,给一个默认值,如果用户想要更改缓存变量的值,可以通过cmake -D的形式去更改.
cmake变量的作用域
做C/C++开发,作用域并不陌生.作用域和变量是密切相关的.cmake有变量自然有作用域的概念.不过cmake作用域可不简单针对变量.
cmake有策略这么一个概念,策略也有作用域.
在C/C++中我们可以使用{},函数,类等产生新的作用域.同时也有全局作用域的概念.在cmake中,通常在使用add_subdirectory()命令或者定义函数的时候产生新的作用域.自cmake 3.25以后可以使用block()在任意位置产生新的作用域.
cmake环境变量和缓存变变量的作用域是全局的.cmake普通变量的作用域受到不同cmake命令的影响.
在定义cmake普通变量的时候,如果没有PARENT_SCOPE选项.那该变量的作用域就在当CMakeLists.txt中,或者在当前函数,或者当前block()中.如果有PARENT_SCOPEcmake定义的变量就在父作用域.
demo:
set(A "aaa")
A 就在当前的CMakeLists.txt文件中.通过message是可以打印出来的.
如果有PARENT_SCOPE(离开当前作用域才会生效)
set(A "aaa" PARENT_SCOPE)
使用message是打印不出来的
cmake_minimum_required(VERSION 3.25 FATAL_ERROR)
set(A "aaa")
message(STATUS "A=${A}")
set(B "bbb" PARENT_SCOPE)
message(STATUS "B=${B}")
block()
block([SCOPE_FOR [POLICIES] [VARIABLES] ] [PROPAGATE <var-name>...])
<commands>
endblock()
第一个参数是可选的,[SCOPE_FOR [POLICIES] [VARIABLES] ]
POLICIES:策略
VARIABLES变量
PROPAGATE : 传播
该命令需要cmake 大于等于3.25版本,该命令用于创建新的作用域(变量作用域,策略作用域)
block demo:
set (x 1)
block()
set(x 2)
set(y 3)
message(STATUS "x = ${x}")
message(STATUS "y = ${y}")
endblock()
message(STATUS "top x = ${x}")
message(STATUS "top y = ${y}")
x的作用域在外面,在block()里面新建一个x的值,和y的值,在作用域中打印,作用域里面的x和y的值就是当前作用域里面的值,x会把外面作用域的值覆盖了,出了block作用域,block作用域里面的赋值全部失效所以在外面x=1,y未定义为空.
set (x 1 PARENT_SCOPE)
set (y 3 PARENT_SCOPE)
block()
set(x 2 PARENT_SCOPE)
unset(y PARENT_SCOPE)
message(STATUS "x = ${x}")
message(STATUS "y = ${y}")
endblock()
message(STATUS "top x = ${x}")
message(STATUS "top y = ${y}")
这个例子有PARENT_SCOPE关键字,所以在外面定义的x和y的值是在上一层生效,而在block中定义x和取消定义y是也是在上层生效.所x和y在block中打印的值都是空,出了block作用域,在block作用域定义的x和取消y的操作生效了,所以打印x为2 y为空.
set (x 1)
set (z 5)
block(PROPAGATE x z)
set(x 2)
set(y 3)
unset(z)
message(STATUS "x = ${x}")
message(STATUS "y = ${y}")
message(STATUS "z = ${z}")
endblock()
message(STATUS "top x = ${x}")
message(STATUS "top y = ${y}")
message(STATUS "top z = ${z}")
PROPAGATE这个关键字是传播出去的意思.PROPAGATE x 和 z就说明,x和z的在block中的操作被传播出去了.
在顶层设置x和z的值,block中重新赋值x和y取消定义z,所以在block中打印x = 2, y =3 z =空,由于在block外层.未定义y block也未传播出去y,所以x依旧等于2, y和z为空.
set (x 1)
set (z 5)
block(SCOPE_FOR VARIABLES PROPAGATE x z)
set(x 2)
set(y 3)
unset(z)
message(STATUS "x = ${x}")
message(STATUS "y = ${y}")
message(STATUS "z = ${z}")
endblock()
message(STATUS "top x = ${x}")
message(STATUS "top y = ${y}")
message(STATUS "top z = ${z}")
这个和上面的一样SCOPE_FOR VARIABLES不写就是默认值.
function 函数作用域简单 demo
set(A "aaa")
set(B "bbb")
function(text_007)
set(C "ccc")
message(STATUS "test_007 A=${A}")
message(STATUS "test_007 B=${B}")
message(STATUS "test_007 C=${C}")
endfunction()
text_007()
message(STATUS "top test_007 A=${A}")
message(STATUS "top test_007 B=${B}")
message(STATUS "top test_007 C=${C}")
函数作用域,在函数体内可以打印A B C,由于C是在函数体内定义的变量,出了函数体就C就被析构了所以在函数体外C为空.
set(A "aaa" PARENT_SCOPE)
set(B "bbb" PARENT_SCOPE)
function(text_007)
set(C "ccc" PARENT_SCOPE)
message(STATUS "test_007 A=${A}")
message(STATUS "test_007 B=${B}")
message(STATUS "test_007 C=${C}")
endfunction()
text_007()
message(STATUS "top test_007 A=${A}")
message(STATUS "top test_007 B=${B}")
message(STATUS "top test_007 C=${C}")
PARENT_SCOPE,在上一层起作用,函数体内的C定义了C,但是在函数体内不起作用在函数体外起作用.所以C在外层有值,AB在哪里都没有值.
set(A "aaa" PARENT_SCOPE)
set(B "bbb")
function(text_007)
unset(B)
set(C "ccc" PARENT_SCOPE)
message(STATUS "test_007 A=${A}")
message(STATUS "test_007 B=${B}")
message(STATUS "test_007 C=${C}")
endfunction()
text_007()
message(STATUS "top test_007 A=${A}")
message(STATUS "top test_007 B=${B}")
message(STATUS "top test_007 C=${C}")
B在函数体内设置未定义,所以在函数体内为空,但是在函数体外是可以打印的.
set(A "aaa" PARENT_SCOPE)
set(B "bbb")
function(text_007)
unset(B PARENT_SCOPE)
set(C "ccc" PARENT_SCOPE)
message(STATUS "test_007 A=${A}")
message(STATUS "test_007 B=${B}")
message(STATUS "test_007 C=${C}")
endfunction()
text_007()
message(STATUS "top test_007 A=${A}")
message(STATUS "top test_007 B=${B}")
message(STATUS "top test_007 C=${C}")
B在函数体内设置未定义,但是在上一次生效,所以在函数体内打印的不受影响,但是在函数体外B为空.
高级变量
mark_as_advanced([CLEAR|FORCE] <var1> ...)
这个在cmake_gui里可以看到的, 第一个参数有两个,
CLEAR: 不标记为高级变量
FORCE: 标记为高级变量
如果这两个关键字都没有,那只有在该变量从未标记过高级变量或非高级变量时才将其标记为高级变量的
总结:
PARENT_SCOPE修饰的变量在上一层生效,函数体内对变量的操作不会对体外变量产生影响,除非使用PARENT_SCOPE.
block的PROPAGATE关键字导出,这个是可以在block中修改外部变量的,如果没有PROPAGATE这个变量和function作用差不多.