CMake. Create coverage target - c++

Sorry for such popular question. But I can't correspondingly apply answers from here to my environment.
I have api and tests to it. Both are subprojects to main "dummy" project. I stuck because I used CMake-anitpattern:
cmake_minimum_required (VERSION 2.8)
set(CMAKE_SKIP_RPATH FALSE)
add_subdirectory ( src )
add_subdirectory ( test )
add_custom_target(coverage
COMMAND make
COMMAND sh ${CMAKE_SOURCE_DIR}/do_coverage.sh
)
That COMMAND make is bad solution because I planning to build my project on Windows later (yep, I need put commands from sh-script in CMakeLists.txt for this purpose too).
So, how can I let CMake to build test project in automatic mode for doing coverage things (gcov, gcovr) even if after cmake I want to make coverage straightway?
Thanks!

you may use cmake to build it like this:
add_custom_target(
coverage
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}
COMMAND sh ${CMAKE_SOURCE_DIR}/do_coverage.sh
)

Related

How can make CMake install an external project my targets depend on? [duplicate]

It can be a pain to refrence ExternalProjects when their install targets are messed up. So one may want to build and install ExternalProjects once before generating main project files for given project. Is it possible with CMake and how to do it?
You may use cmake call within execute_process for configure and build CMake project, which contains ExternalProject:
other_project/CMakeLists.txt:
project(other_project)
include(ExternalProject)
ExternalProject_Add(<project_name> <options...>)
CMakeLists.txt:
# Configure external project
execute_process(
COMMAND ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR}/other_project
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/other_project
)
# Build external project
execute_process(
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}/other_project
)
Such a way other_project will be configured and built in directory ${CMAKE_BINARY_DIR}/other_project. If you do not disable installation in ExternalProject_Add call, then it will performed when building other_project.
Normally, you want some options to ExternalProject, like SOURCE_DIR, BINARY_DIR, INSTALL_DIR, to be deduced from variables in the main project. You have two ways for achive that:
Create CMakeLists.txt for other_project with configure_file, called from main project (before execute_process command).
Pass variables from main project as -D parameters to ${CMAKE_COMMAND}.
Having separated execute_process calls for sequential COMMANDS is important. Otherwise, if use single execute_process with several COMMANDS, these commands will be just "piped" (executed concurrently but with output of the first command being treated as input for the second).

Using Cmake to just do ./genMakefiles && make -j4

I'm trying to integrate CMake build on https://github.com/rgaufman/live555
It uses the traditional ./genMakefiles && make -j4 therefore I just need to create a CMakeLists.txt that does this, right?
I know the right way would be to add all the cpp files and build everything but it's not my project and I'm not going to maintain it so the best way for this case would be to just integrate like I'm suggesting
Here's my prototype:
cmake_minimum_required(VERSION 3.9)
project(live555)
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/live555
COMMAND ./genMakefiles linux
COMMENT "=================== Generating makefiles for linux"
COMMAND make -j4
COMMENT "=================== make..."
)
I didn't understand what is OUTPUT for. Isn't OUTPUT dictated by make -j4? Also, I did cmake . && make, see the output:
lz#vm:~/JSCam/src/jscam/live555$ cmake .
-- Configuring done
-- Generating done
-- Build files have been written to: /home/lz/JSCam/src/jscam/live555
lz#vm:~/JSCam/src/jscam/live555$ make
I can't either see the comments I added and files aren't being built.
In CMake, if you need to build some other project which you are "not going to maintain", ExternalProject_Add usually is a good choice:
The ExternalProject_Add function creates a custom target to drive download, update/patch, configure, build, install and test steps of an external project
Its usage is quite simple:
include(ExternalProject) # Include definition of 'ExternalProject_Add' function
ExternalProject_Add(live555 # Name of the target. Could be any
# Setup source directory
SOURCE_DIR <path-to-directory-with-external-project>
# Setup build directory. Here it is the same as source one.
BUILD_IN_SOURCE 1
# Configuration step
CONFIGURE_COMMAND ./genMakefiles linux
# Build step. It is actually `make`, but in a wise manner.
BUILD_COMMAND ${CMAKE_MAKE_COMMAND}
# Disable install step
INSTALL_COMMAND ""
)

Running Google test at build time with CMake generated system

My configuration has CMake 3.6, Visual Studio 2015 and latest Google test from GitHub. I add my unit tests through one of my cmake functions addGtest and do the build. After this I can run the test from my RUN_TESTS target or using ctrl + F5 in VS and works as expected.
The final goal is to run the unit tests at build time using the CMake dependency management. For now, as a first step, I have enhanced my function to create a custom_target (included the entire function, in case there are unforeseen issues in the working part), but not build it:
function (addGtest)
# vvvv this part works as explained vvvv #
set (optBOOLS)
set (optSINGLES EXE)
set (optLISTS DLL_LIST)
cmake_parse_arguments (myARGS
"${optBOOLS}" "${optSINGLES}" "${optLISTS}" ${ARGN})
# addExecutable is a function that adds target executables
set(myARGS_DLL_LIST gtest_main gtest "${myARGS_DLL_LIST}")
addExecutable (EXE ${myARGS_EXE} DLL_LIST ${myARGS_DLL_LIST} ${myARGS_UNPARSED_ARGUMENTS})
add_test (NAME ${myARGS_EXE} COMMAND ${myARGS_EXE} WORKING_DIRECTORY
${CMAKE_INSTALL_PREFIX}/$<$<CONFIG:Release>:Release>$<$<CONFIG:Debug>:Debug>/bin
) # so it can be run using ctest
# ^^^^ this part works as explained ^^^^ #
add_custom_target (${myARGS_EXE}.tgt DEPENDS ${myARGS_EXE}
COMMAND ${myARGS_EXE} --gtest_output="xml:${myARGS_EXE}.xml"
WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/$<$<CONFIG:Release>:Release>$<$<CONFIG:Debug>:Debug>/bin
)
endfunction (addGtest)
As expected when I perform the build, a new target, say, utMyTest.tgt is added to VS, but it is not built. Now when I build this new target by hand in VS, I expect that the test will be run. But it doesn't and gives the following error:
1> The filename, directory name, or volume label syntax is incorrect.
I tried providing full path to the COMMAND option, removing double quotes around --gtest_output value, but to no avail. On the other hand when I cd to the working directory in a command line window and invoke the exe, it works fine!!
The first question is how do I fix it to run the test by building this new target? After that, I plan to add_custom_target (${myARGS_EXE}.run) and add_dependencies (${myARGS_EXE}.run ${myARGS_EXE}.tgt). Would this then run the test whenever the exe changes? Or should I do something else? Thank you for your help.
Could not add so much details in the comment, hence this answer.
1. Answer to the original problem
Since I needed the configuration dependent path in the WORKING_DIRECTORY option of the add_custom_target command, but cannot pass generator expressions to it, the idea is to use the CMAKE_CFG_INTDIR variable so:
add_custom_target (${myARGS_EXE}.tgt
DEPENDS ${myARGS_EXE}
COMMAND ${myARGS_EXE} --gtest_output=xml:${myARGS_EXE}.xml
WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/${CMAKE_CFG_INTDIR}/bin
)
Now, when you build the above target, the unit test is run in the WORKING_DIRECTORY which is not entirely desirable, since that is the install directory for libs and exes. It would be really nice to ...
2. Run the unit test from its build directory
While, at the same time, picking up the DLL paths from within Visual Studio, and storing the Gtest generated .xml file in the build directory. This is the solution:
In CMake version 3.10 CMAKE_MSVCIDE_RUN_PATH property was added. In the project wide CMakeLists.txt, set(CMAKE_MSVCIDE_RUN_PATH ${CMAKE_INSTALL_PREFIX}/${CMAKE_CFG_INTDIR}/bin) - thanks to this solution #3, we can appendPATH to point to our install directory. And then replace the above add_custom_target command with this:
add_custom_command (
TARGET ${myARGS_EXE} POST_BUILD
COMMAND ${myARGS_EXE} --gtest_output=xml:${myARGS_EXE}.xml
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}
)
This solution avoids the mess of creating additional targets. Clearly only when myARGS_EXE is built, the unit test is run. Obviously myARGS_EXE's transitive dependency on other DLLs is covered also.
If you have other elegant solutions, please post.

How to build cmake ExternalProject while configurating main one?

It can be a pain to refrence ExternalProjects when their install targets are messed up. So one may want to build and install ExternalProjects once before generating main project files for given project. Is it possible with CMake and how to do it?
You may use cmake call within execute_process for configure and build CMake project, which contains ExternalProject:
other_project/CMakeLists.txt:
project(other_project)
include(ExternalProject)
ExternalProject_Add(<project_name> <options...>)
CMakeLists.txt:
# Configure external project
execute_process(
COMMAND ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR}/other_project
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/other_project
)
# Build external project
execute_process(
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}/other_project
)
Such a way other_project will be configured and built in directory ${CMAKE_BINARY_DIR}/other_project. If you do not disable installation in ExternalProject_Add call, then it will performed when building other_project.
Normally, you want some options to ExternalProject, like SOURCE_DIR, BINARY_DIR, INSTALL_DIR, to be deduced from variables in the main project. You have two ways for achive that:
Create CMakeLists.txt for other_project with configure_file, called from main project (before execute_process command).
Pass variables from main project as -D parameters to ${CMAKE_COMMAND}.
Having separated execute_process calls for sequential COMMANDS is important. Otherwise, if use single execute_process with several COMMANDS, these commands will be just "piped" (executed concurrently but with output of the first command being treated as input for the second).

Generator expressions cmake: copying works in debug but not release mode

I am trying to figure out how to copy some libs depending on the config in cmake.
I tried this:
add_custom_command(TARGET Myapp
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<$<CONFIG:Debug>:${_LIBS_DEBUG}>
$<$<CONFIG:Release>:${_LIBS_RELEASE}>
$<TARGET_FILE_DIR:MyApp>)
It copies libs in Debug but not in release:
Is this supposed to be legal and should work?
If it is not legal (I do not get error), how can I achieve the same effect?
Turning my comments into an answer
What I normally do to debug those case is to add another COMMAND before the actual line in question that just echos the command line. In your case:
COMMAND ${CMAKE_COMMAND} -E echo
$<$<CONFIG:Debug>:${_LIBS_DEBUG}>
$<$<CONFIG:Release>:${_LIBS_RELEASE}>
I've run this a few tests and you will see that the $<1:...> and $<0:...> expressions are not evaluated.
So seeing this I was searching CMake's bug tracker database and this is a known issue and yet (as for CMake 3.5.2) unresolved: 0009974: CMake should support custom commands that can vary by configuration.
There are several ways proposed in this ticket that do work with existing versions of CMake.
In your case - until this issue is resolved and if you want to have it shell independent - I would do it the "old way" and call a CMake script:
CopyLibsByConfig.cmake.in
if (_CONFIG STREQUAL "Debug")
file(COPY #_LIBS_DEBUG# DESTINATION "${_DEST_PATH}")
else()
file(COPY #_LIBS_RELEASE# DESTINATION "${_DEST_PATH}")
endif()
CMakeLists.txt
...
configure_file(CopyLibsByConfig.cmake.in CopyLibsByConfig.cmake #ONLY)
add_custom_command(TARGET MyApp
POST_BUILD
COMMAND ${CMAKE_COMMAND}
-D _CONFIG=$<CONFIG>
-D _DEST_PATH="$<TARGET_FILE_DIR:MyApp>"
-P "${CMAKE_CURRENT_BINARY_DIR}/CopyLibsByConfig.cmake"
)
But the solution can very much depend on the files you want to copy to your binary output folder. And there are a lot of way doing it, like using install():
install(FILES ${_LIBS_DEBUG} CONFIGURATIONS Debug DESTINATION $<TARGET_FILE_DIR:MyApp>)
install(FILES ${_LIBS_RELEASE} CONFIGURATIONS Release DESTINATION $<TARGET_FILE_DIR:MyApp>)
set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
Obviously that's not the way install() is meant to be used, so consider using the INSTALL or PACKAGE targets properly to distribute your application and all its dependencies.
And if we are talking about Visual Studio runtime DLLs you most likely want to take a look at the InstallRequiredSystemLibraries CMake module.
Other solution is to use generator expression.
For example I have cppzmq (shared library) and cppzmq-static (static library with static dependencies). I would like to have faster debug builds so I use cppzmq in Debug build and in (other) e.g. release I want one big fat exec.
target_link_libraries(CommunicationCommonLib PUBLIC
$<IF:$<CONFIG:Debug>,cppzmq,cppzmq-static>
Dexode::EventBus
gcpp
protobuf::libprotobuf
)