在子目录中调用 target_link_libraries 的替代方法

Alternative to calling target_link_libraries in subdirectory

案例:

我在单独的 .cmake 脚本中声明了一个函数 setup_target_for_coverage,添加到 CMAKE_MODULE_PATH,它准备了一个目标 (mylib) 用于代码覆盖率分析。 setup_target_for_coverage 从子目录调用到 mylib (mylib_tests)。

要求是所有与测试和代码覆盖有关的事情,仅针对此目标,发生在 mylib/testssetup_target_for_coverage,从 mylib/CMakeLists.txt 我们只添加测试子目录和 仅此而已。最后,mylib 不知道测试细节或代码覆盖率的存在,它只是 "blindly" 调用 add_subdirectory( tests ).

示例:

看起来像这样:

我的库:

# ./mylib/CMakeLists.txt:

project( mylib )
add_library( mylib main.cpp )
# This is all mylib should be aware of, everything else is hidden
add_subdirectory( tests )

mylib_tests:

# ./mylib/tests/CMakeLists.txt:

project( mylib_tests )

# ...

include(code-coverage)
setup_target_for_coverage(
    TARGET mylib
    TEST_RUNNER mylib_tests
)

代码覆盖率:

# ./cmake/code-coverage.txt:

function(setup_target_for_coverage)
    # Parse arguments...

    # Setting compile flags: Works as expected
    target_compile_options( ${args_TARGET}
        PRIVATE
            -g -O0 --coverage
    )

    # Setting linker flags: THIS FAILS <---------------
    target_link_libraries( ${args_TARGET}
        PRIVATE
            --coverage
    )

    add_custom_target( ${args_TARGET}_coverage}
        # Setup lcov, run "TEST_RUNNER", generate html...
        COMMAND ...
    )

endfunction()

简而言之,它设置了编译器和链接器标志 (--coverage) 并添加了一个运行 gcov/lcov 和 genhtml 的自定义目标。

问题:

问题出在 target_link_libraries,(如文档所述)需要在创建目标的同一 "directory" 中发生:

The named <target> must have been created in the current directory by a command such as add_executable() or add_library() [...]

因此出现以下错误:

Attempt to add link library "--coverage" to target "mylib" which is not built in this directory.

我试图通过直接使用 set_property 来解决这个问题

set_property(
    TARGET ${TARGET} 
    APPEND_STRING PROPERTY LINK_FLAGS "--coverage"
)

无济于事。它失败并出现以下错误,即使在相同的 CMakeLists.txt:

"undefined reference to `__gcov_merge_add'"

如果将 target_link_libraries 移动到 ./mylib/CMakeLists.txt,它会起作用,但如前所述,这不符合我的要求。

有什么想法吗?

function替换为macro:

macro(setup_target_for_coverage)
  [...]
endmacro()

CMake 宏类似于 C 宏:您可以将其视为文本替换,因此它的行为就像代码位于原始文件中一样。

将评论变成答案。正如您所强调的,您只能在同一目录范围内定义的目标上调用 target_link_libraries() 。当您调用 add_subdirectory() 时,您进入了一个新的目录范围,因此遇到了您询问的问题。

另一方面,include() 命令不会创建新的目录范围。这有两个直接相关的影响:

  • included 目录的文件中,您仍然可以为 including 目录范围内的目标调用 target_link_libraries()
  • CMAKE_CURRENT_SOURCE_DIR 的值没有改变,因此您可能希望在包含的文件中使用 CMAKE_CURRENT_LIST_DIR。请注意,CMAKE_CURRENT_BINARY_DIR 没有等效项,它也不会改变。

一个典型的模式可能是这样的:

mylib/CMakeLists.txt:

add_library( mylib main.cpp )
include( tests/CMakeLists.txt )

mylib/tests/CMakeLists.txt:

add_executable( someTest "${CMAKE_CURRENT_LIST_DIR}/someTest.cpp" )
# ...

也许至少是切线相关的,您可能还会对 this article 中的想法感兴趣。它展示了如何跨目录管理源和目标,一路上涉及 add_subdirectory()include() 的讨论。