在 CMake 中转发无值函数参数的简单方法

Simple way to forward a no-value function parameter in CMake

我有两个 CMake 函数(我们称它们为 innerouter),它们使用 cmake_parse_arguments 和相同的选项(无值参数)XXX 和一些其他参数,所以它们看起来像

function(inner)
    set(prefix ARG)
    set(noValues XXX)
    set(singleValues ...)
    set(multiValues ...)
    cmake_parse_arguments(${prefix}
                          "${noValues}"
                          "${singleValues}"
                          "${multiValues}"
                          ${ARGN})
...
endfunction()

function(outer)
    set(prefix ARG)
    set(noValues XXX)
    set(singleValues ...)
    set(multiValues ...)
    cmake_parse_arguments(${prefix}
                          "${noValues}"
                          "${singleValues}"
                          "${multiValues}"
                          ${ARGN})
...
    inner(
        $<$<BOOL:${ARG_XXX}>:XXX>
        ...)
endfunction()

目的是如果在调用outer时设置了XXX,它将被传递给inner。但这似乎不起作用;如果我添加

# in outer
    message("in outer, FILE=${ARG_FILE}, XXX=${ARG_XXX}")

# in inner
    message("in outer, FILE=${ARG_FILE}, XXX=${ARG_XXX}")

FILE 是另一个通过的参数,但它是一个强制性的单值参数,所以只有 FILE "${ARG_FILE} 效果很好)我明白了

in outer, FILE=abc.def, XXX=TRUE
in inner, FILE=abc.def, XXX=FALSE

为什么这不像我预期的那样工作,正确的方法是什么?

当然可以

if (ARG_XXX)
    inner(XXX ...all the other arguments...)
else()
    inner(...all the other arguments...)
endif()

但我希望这不是必需的,因为实际的代码库不仅有 2 层,每个层有 1 个函数...

CMake 版本为 3.15.5,但答案必须适用于 3.10.2 及更高版本。

这里的想法是使用 CMake 的列表扩展参数传递语法。当您编写 func(${args}) 时,它将使用列表 ${args} 元素 来构建对 func 的调用。例如,如果您有 set(args "1" "2" "3"),则 func(${args})func("1" "2" "3") 相同,但如果您 引用 func 的参数那么这不会发生,即 func("${args}") == func("1;2;3").

所以在这种情况下,我们只是将您的 TRUEFALSE 变量 ARG_XXX 转换为局部变量 XXX,即 XXX 或空 true/false,然后调用 inner(${XXX}).

if (ARG_XXX)
    set(XXX XXX)
else ()
    set(XXX "")
endif ()

inner(${XXX})

据我所知没有更好的方法。这是一个完整的演示:

# test.cmake
cmake_minimum_required(VERSION 3.9)

function(inner)
    cmake_parse_arguments(ARG "XXX" "" "" ${ARGN})
    message(STATUS "inner: ARG_XXX = ${ARG_XXX}")
endfunction()

function(outer)
    cmake_parse_arguments(ARG "XXX" "" "" ${ARGN})

    message(STATUS "outer: ARG_XXX = ${ARG_XXX}")

    if (ARG_XXX)
        set(xxx XXX)
    else ()
        set(xxx "")
    endif ()

    inner(${xxx})
endfunction()

outer()
outer(XXX)

运行 和 cmake -P test.cmake 这将打印:

$ ~/Downloads/cmake-3.9.6-Linux-x86_64/bin/cmake -P test.cmake
-- outer: ARG_XXX = FALSE
-- inner: ARG_XXX = FALSE
-- outer: ARG_XXX = TRUE
-- inner: ARG_XXX = TRUE

非常 很久以前就可以工作了,但是我的 ~/Downloads 文件夹中有 Kitware-official 静态链接版本 3.9,所以我使用了它。


另一方面,如果inner的每个调用者已经解析了ARG_XXX,那么你可以使用CMake的动态作用域规则来查看ARG_XXX 在调用者中设置。当你调用一个函数时,CMake 复制变量环境并将其传递给被调用者。因此,以下内容也将起作用:

cmake_minimum_required(VERSION 3.9)

function(inner)
    cmake_parse_arguments(ARG "" "" "" ${ARGN})
    message(STATUS "inner: ARG_XXX = ${ARG_XXX}")
endfunction()

function(outer)
    cmake_parse_arguments(ARG "XXX" "" "" ${ARGN})
    message(STATUS "outer: ARG_XXX = ${ARG_XXX}")

    inner()
endfunction()

outer()
outer(XXX)

在这种情况下,inner 依赖于 outer 来解析 XXX 选项。显然,如果 inner 可能被任意函数调用(并且将 ARG_XXX 设置为合理的值成为其合同的一部分),这将是相当脆弱的。