Finally integrating Gcov and Lcov tool into Cppagent build process

Subhendu Ghosh
codeflu
Published in
4 min readJan 16, 2015

This is most probably my final task on Implementing Code Coverage Analysis for Mtconnect Cppagent. In my last post i showed you the how the executable files are generated using Makefiles. In Cppagent the Makefiles are actually autogenerated by a cross-platform Makefile generator tool CMake. To integrate Gcov and Lcov into the build system we actually need to start from the very beginning of the process which is cmake. The CMake commands are written in CmakeLists.txt files. A minimal cmake file could look something like this. Here we have the test_srcs as the source file and agent_test as the executable.

[code lang=”bash”]

cmake_minimum_required (VERSION 2.6)

project(test)

set(test_srcs menu.cpp)

add_executable(agent_test ${test_srcs})

[/code]

Now lets expand and understand the CMakeLists.txt for cppagent.

[code lang=”bash”]set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../agent/CMake;${CMAKE_MODULE_PATH}") [/code]

This sets the path where cmake should look for files when files or include_directories command is used. The set command is used to set values to the variables. You can print all the available variable out using the following code.

[code lang=”bash”]
get_cmake_property(_variableNames VARIABLES)
foreach (_variableName ${_variableNames})
message(STATUS "${_variableName}=${${_variableName}}")
endforeach()
[/code]

source: stackoverflow.com

Next section of the file:

if(WIN32)
set(LibXML2_INCLUDE_DIRS ../win32/libxml2-2.9/include )

if(CMAKE_CL_64)
set(bits 64)
else(CMAKE_CL_64)
set(bits 32)
endif(CMAKE_CL_64)

file(GLOB LibXML2_LIBRARIES "../win32/libxml2-2.9/lib/libxml2_a_v120_${bits}.lib")
file(GLOB LibXML2_DEBUG_LIBRARIES ../win32/libxml2-2.9/lib/libxml2d_a_v120_${bits}.lib)
set(CPPUNIT_INCLUDE_DIR ../win32/cppunit-1.12.1/include)
file(GLOB CPPUNIT_LIBRARY ../win32/cppunit-1.12.1/lib/cppunitd_v120_a.lib)
endif(WIN32)

Here, we are checking the platform we are working on and accordingly the library variables are being set to the windows based libraries. We will discuss the file command later.

if(UNIX)
execute_process(COMMAND uname OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE CMAKE_SYSTEM_NAME)
if(CMAKE_SYSTEM_NAME MATCHES Linux)
set(LINUX_LIBRARIES pthread)
endif(CMAKE_SYSTEM_NAME MATCHES Linux)
endif(UNIX)

Next if the OS platform is Unix based then we execute the command uname as child-process and store the output in CMAKE_SYSTEM_NAME variable. If its a Linux environment., Linux will be stored in the CMAKE_SYSTEM_NAME variable, hence, we set the variable LINUX_LIBRARIES to pthread(which is the threading library for linux). Now we find something similar we did in our test CMakeLists.txt. The project command sets the project name, version etc. The next line stores the source file paths to a variable test_src

[code lang=”bash”]set( test_srcs file1 file2 …)[/code]

Now we will discuss about the next few lines.

file(GLOB test_headers *.hpp ../agent/*.hpp)

The file command is used to manipulate the files. You can read, write, append files, also GLOB allows globbing of files which is used to generate a list of files matching the expression you give. So here wildcard expression is used to generate a list of all header files in the particular folder *.hpp.

include_directories(../lib ../agent .)

This command basically tells cmake to add the directories specified by it to its list of directories when looking for a file.

find_package(CppUnit REQUIRED)

This command looks for package and loads the settings from it. REQUIRED makes sure the External package is loaded properly else it must stop throwing an error.

add_definitions(-DDLIB_NO_GUI_SUPPORT ${LibXML2_DEFINITIONS})

add_definitions is where the additional compile time flags are added.

add_executable(agent_test ${test_srcs} ${test_headers})

This line generates an executable target for the project named agent_test and test_src and test_headers are its source and header files respectively.

target_link_libraries(agent_test ${LibXML2_LIBRARIES} ${CPPUNIT_LIBRARY} ${LINUX_LIBRARIES})

This line links the executable its libraries.

::Gcov & Lcov Integration::

Now that we know our CMake file well, lets make the necessary changes.

Step #1

Add two variables and set the appropriate compile and linking flags for gcov and lcov respectively.

[code lang=”bash”]set(GCOV_COMPILE_FLAGS "-fprofile-arcs -ftest-coverage")
set(GCOV_LINK_FLAGS "-lgcov")[/code]

Step #2

Split the source into two halves one being the unit test source files and the other being the cppagent source files. We are not interested in unit test files’ code coverage.

set( test_srcs test.cpp
adapter_test.cpp
agent_test.cpp
checkpoint_test.cpp
config_test.cpp
component_test.cpp
component_event_test.cpp
connector_test.cpp
data_item_test.cpp
device_test.cpp
globals_test.cpp
xml_parser_test.cpp
test_globals.cpp
xml_printer_test.cpp
asset_test.cpp
change_observer_test.cpp
cutting_tool_test.cpp
)
set(agent_srcs ../agent/adapter.cpp
../agent/agent.cpp
../agent/checkpoint.cpp
../agent/component.cpp
../agent/component_event.cpp
../agent/change_observer.cpp
../agent/connector.cpp
../agent/cutting_tool.cpp
../agent/data_item.cpp
../agent/device.cpp
../agent/globals.cpp
../agent/options.cpp
../agent/xml_parser.cpp
../agent/xml_printer.cpp
../agent/config.cpp
../agent/service.cpp
../agent/ref_counted.cpp
../agent/asset.cpp
../agent/version.cpp
../agent/rolling_file_logger.cpp
)

Step #3

Like i told in Step 2 we are not interested in unit test source files. So here we just add the Gcov compile flags to only the cppagent source files. So .gcno files of only the agent source files are generated.

[code lang=”bash”]set_property(SOURCE ${agent_srcs} APPEND PROPERTY COMPILE_FLAGS ${GCOV_COMPILE_FLAGS})[/code]

Step #4

Now we also know that for coverage analysis we need to link the “lgcov” library. Therefore, we do this in the following way.

[code lang=”bash”]target_link_libraries(agent_test ${LibXML2_LIBRARIES} ${CPPUNIT_LIBRARY} ${LINUX_LIBRARIES} ${GCOV_LINK_FLAGS}) [/code]

Step #5

Since we love things to be automated. I added a target for the make command to automate the whole process of running test and copying the “.gcno” files and moving the “.gcda” files to a folder then running the lcov command to read the files and prepare a easily readable statistics and finally the genhtml command to generate the html output. add_custom_target allows you to add custom target for make(Here i added “cov” as the target name). COMMAND allows you to specify simple bash commands.

[code lang=”bash”]add_custom_target( cov
COMMAND [ -d Coverage ]&&rm -rf Coverage/||echo "No folder"
COMMAND mkdir Coverage
COMMAND agent_test
COMMAND cp CMakeFiles/agent_test.dir/__/agent/*.gcno Coverage/
COMMAND mv CMakeFiles/agent_test.dir/__/agent/*.gcda Coverage/
COMMAND cd Coverage&&lcov -t "result" -o cppagent_coverage.info -c -d .
COMMAND cd Coverage&&genhtml -o coverage cppagent_coverage.info
COMMENT "Generated Coverage Report Successfully!"
)

[/code]

::Conclusion::

Now to build test and generate report.

Step #1 cmake .    // In project root which cppagent/
Step #2 cd test // since we want to build only test
Step #3 make // This will build the agent_test executable.
Step #4 make cov // Runs test, Copies all files to Coverage folder, generates report.

So, we just need to open the Coverage/coverage/index.html to view the analysis report. Final file will look something like this.

--

--