0%

CMake 之 set

引言

CMake 版本取自于 3.21.2。

set

set 用来将给定的值保存到常规变量、缓存条目、环境变量中。命令的签名中,占位符 <value>... 用于指定 0 个或者多个参数。如果多个参数被指定,那么它们将会以 ; 连接起来作为实际的参数。如果没有任何参数被指定,那么该变量就会被注销(类似于 unset 命令)。

Set Normal Variable

1
set(<variable> <value>... [PARENT_SCOPE])

在当前函数或者目录范围内设置指定值到变量中。
如果使用了 PARENT_SCOPE 选项,意味着该变量的作用域会传递到上一层(即上一层目录或者当前函数的调用者),变量的先前状态在当前作用域中保持不变(换句话说如果变量先前未定义,那么现在也仍然未定义,反之亦然)。

📚Tips

每一个新的目录或者函数都会创建一个新的作用域,普通变量的作用域,如果不指定 PARENT_SCOPE 选项,只能从外层往内层传递。

示例

  1. 函数内部定义变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    cmake_minimum_required(VERSION 3.21.2)
    project(SET_TEST)


    function(test_set_fn value)
    set(VAL_IN_FN ${value} PARENT_SCOPE)
    set(VAL_IN_FN_DEFAULT_SCOPE ${value})
    message(STATUS "the val in fn is : ${VAL_IN_FN}")
    message(STATUS "the val with default scope in fn is : ${VAL_IN_FN_DEFAULT_SCOPE}")
    endfunction(test_set_fn)

    test_set_fn("hello")

    message(STATUS "the val out of the fn is : ${VAL_IN_FN}")
    message(STATUS "the val with default scope out of fn is : ${VAL_IN_FN_DEFAULT_SCOPE}")

    ---output:

    -- the val in fn is :
    -- the val with default scope in fn is : hello
    -- the val out of the fn is : hello
    -- the val with default scope out of fn is :

    我们在函数 test_set_fn 中定义两个变量:具有 PARENT_SCOPE 作用域的 VAL_IN_FN 以及默认作用域的 VAL_IN_FN_DEFAULT_SCOPE。我们在函数内部输出两个变量的值,在函数外部输出两个变量。

    通过结果我们发现,当指定 PARENT_SCOPE 时,函数内部无法显示该值,而函数调用所在的文件可以访问。对于具有默认作用域的变量,却相反。

    当然,如果不调用函数的话,那两个变量都没有。

  2. 在函数内部调用函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    cmake_minimum_required(VERSION 3.21.2)
    project(SET_TEST)


    function(test_set_fn value)
    set(VAL_IN_FN ${value} PARENT_SCOPE)
    set(VAL_IN_FN_DEFAULT_SCOPE ${value})
    message(STATUS "the val in fn is : ${VAL_IN_FN}")
    message(STATUS "the val with default scope in fn is : ${VAL_IN_FN_DEFAULT_SCOPE}")
    endfunction(test_set_fn)

    function(print)
    test_set_fn("hello")
    message(STATUS "-----------------------")
    message(STATUS "print : the val out of the fn is : ${VAL_IN_FN}")
    message(STATUS "print :the val with default scope out of fn is : ${VAL_IN_FN_DEFAULT_SCOPE}")
    endfunction(print)

    print()

    message(STATUS "-----------------------")
    message(STATUS "the val out of the fn is : ${VAL_IN_FN}")
    message(STATUS "the val with default scope out of fn is : ${VAL_IN_FN_DEFAULT_SCOPE}")

    ---output:

    -- the val in fn is :
    -- the val with default scope in fn is : hello
    -- -----------------------
    -- print : the val out of the fn is : hello
    -- print :the val with default scope out of fn is :
    -- -----------------------
    -- the val out of the fn is :
    -- the val with default scope out of fn is :

    本次,我们定义另外一个函数 print 并在该函数中调用 test_set_fn,同时我们也在该函数中输出两个变量。通过对结果分析,我们发现 test_set_fn 函数内部与之前保持一致,只有默认作用域的可以访问。而在当前文件中两个变量都获取不到,但是在 print 函数内部却可以访问到具有 PARENT_SCOPE 作用域的变量。

    也就是说,当变量在函数中声明为 PARENT_SCOPE 时,只会传递给该函数的调用者,如果调用者是一个函数,那么会传递给该函数,如果该调用者是目录,则会传递给当前目录。

  3. 路径中的变量传递

    父级 CMakeLists.txt

    1
    2
    3
    4
    5
    6
    7
    cmake_minimum_required(VERSION 3.21.2)
    project(SET_TEST)

    add_subdirectory(src)

    message(STATUS "the val in directory is : ${VAL_IN_DIRECTORY}")
    message(STATUS "the val with default scope is : ${VAL_IN_DIRECTORY_DEFAULT_SCOPE}")

    子级 CMakeLists.txt

    1
    2
    message(STATUS "sub: the val in directory is : ${VAL_IN_DIRECTORY}")
    message(STATUS "sub : the val with default scope is : ${VAL_IN_DIRECTORY_DEFAULT_SCOPE}")

    输出:

    1
    2
    3
    4
    -- sub: the val in directory is : 
    -- sub : the val with default scope is : hello
    -- the val in directory is : hello
    -- the val with default scope is :

    变量的获取必须要在 add_subdirectory 之后获取。在父级目录中可以获取 PARENT_SCOPE 的变量而获取不到默认作用域的变量,在子目中中则相反。

    Set Cache Entry

    1
    set(<variable> <value>... CACHE <type> <docstring> [FORCE])

    设置缓存条目 <variable><value>,除非使用选项 FORCE,否则默认情况下缓存条目的值不会被覆盖。
    <type> 必须指定为以下几种类型:

  • BOOL
    布尔值 ON/OFF,CMake 的 GUI 界面对此类缓存条目会提供一个复选框。
  • FILEPATH
    文件路径,CMake 的 GUI 界面对此类缓存条目会提供一个文件选择框。
  • PATH
    目录路径,CMake 的 GUI 界面对此类缓存条目会提供一个目录选择框。
  • STRING
    文本行,CMake 的 GUI 界面对此类缓存条目会提供一个文本框(对应 STRING )或下拉选择框(对应 STRINGS)。
  • INTERNAL
    文本行,但是只用于内部,不对外呈现。主要用于运行过程中存储变量,因此使用该 type 意味着使用 FORCE

<docstring> 必须指定为一行文本,用于提供向 CMake 的 GUI 界面展示选项的快速摘要。
如果在调用之前缓存条目不存在或给定了 FORCE 选项,则缓存条目将设置为给定值。

注意事项

  • 如果变量先前未定义或者使用了 FORCE 选项,则缓存条目会直接被赋值。
  • 可以在使用 CMake 构建的使用通过 -D 选项来给缓存条目赋值,这样 CMakeLists.txt 内的 set 命令只会为缓存条目添加类型。
  • 如果变量类型是目录或者文件路径,通过 -D 选项传入的若只是相对路径,那么 set 会给这个相对路径前添加当前的工作目录以变成绝对路径(如果已经是绝对路径则不会处理)。

📚Tips

如果有相同名字的常规变量存在,那么是不能直接获取缓存条目的。如果把策略 CMP0126 设置为 OLD,那么当前作用域内绑定的常规变量都会被删除。

Set Environment Variable

1
set(ENV{<variable>} [<value>])

设置环境变量,之后调用 $ENV{<variable>} 将返回新值。
注意事项
环境变量设置的几个注意事项:

  • 该命令设置的环境变量只在当前的 CMake 进程生效,既不会影响调用者的环境变量,也不会影响系统环境变量。
  • 如果 <value> 为空或者 ENV{<variable>} 后没有参数,则该命令会清除掉当前环境变量的值。
  • <value> 之后额外的参数会被忽略。

参考