在子目录中调用 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/tests
和 setup_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()
的讨论。
案例:
我在单独的 .cmake 脚本中声明了一个函数 setup_target_for_coverage
,添加到 CMAKE_MODULE_PATH
,它准备了一个目标 (mylib
) 用于代码覆盖率分析。 setup_target_for_coverage
从子目录调用到 mylib
(mylib_tests
)。
要求是所有与测试和代码覆盖有关的事情,仅针对此目标,发生在 mylib/tests
和 setup_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 asadd_executable()
oradd_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()
的讨论。