I've declared a function setup_target_for_coverage
in a separate .cmake-script, added to CMAKE_MODULE_PATH
, which prepares a target (mylib
) for code coverage analysis. setup_target_for_coverage
is called from a subdirectory to mylib
(mylib_tests
).
The requirement is that everything that has to do with testing and code coverage, for this target only, happens in mylib/tests
and setup_target_for_coverage
, and from mylib/CMakeLists.txt
we only add the test-subdirectory and nothing more. In the end, neither test-specifics or the existence of code coverage is known to mylib
, it just "blindly" calls add_subdirectory( tests )
.
It looks like this:
mylib:
# ./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
)
code-coverage:
# ./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()
In short, it sets up compiler & linker-flags (--coverage
) and adds a custom target which runs gcov/lcov and genhtml.
The issue is with target_link_libraries
, which (as documented) is required to occur within the same "directory" where the target was created:
The named
<target>
must have been created in the current directory by a command such asadd_executable()
oradd_library()
[...]
I thus get the following error:
Attempt to add link library "--coverage" to target "mylib" which is not built in this directory.
I've tried to get around this by using set_property
directly
set_property(
TARGET ${TARGET}
APPEND_STRING PROPERTY LINK_FLAGS "--coverage"
)
to no avail. It fails with the following error, even if in same CMakeLists.txt:
"undefined reference to `__gcov_merge_add'"
It works if target_link_libraries
is moved to ./mylib/CMakeLists.txt
, but this is as mentioned not fulfilling my requirements.
Any ideas?
Turning comment into an answer. As you've highlighted, you can only call target_link_libraries()
on a target defined in the same directory scope (Edit: this restriction no longer exists since CMake 3.13). When you call add_subdirectory()
, you enter a new directory scope and therefore hit the problem you've asked about.
The include()
command, on the other hand, does not create a new directory scope. This has two immediately relevant effects:
target_link_libraries()
for a target in the including directory's scope.CMAKE_CURRENT_SOURCE_DIR
doesn't change, so you will likely be wanting to use CMAKE_CURRENT_LIST_DIR
instead within the included file. Note that there is no equivalent for CMAKE_CURRENT_BINARY_DIR
which also doesn't change.A typical pattern might be something like this:
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" )
# ...
Perhaps at least tangentially related, you might also find the ideas in this article to be of interest. It shows how to manage sources and targets across directories, touching on the add_subdirectory()
versus include()
discussion along the way.