Detailed guide on using gcov with CMake/CDash? - c++

I'm using CMake with my project and set up a cdash server for continuous/nightly building. Everything works well and by setting up a crontab, we have hourly/nightly build/test results uploaded to our cdash server automatically.
My next step is to add test coverage report to the build. I find the document here https://gitlab.kitware.com/cmake/community/-/wikis/doc/ctest/Coverage but frankly it's a bit far from a practical guide.
Currently I've added the required flag (instead of -fprofile-arcs -ftest-coverage, I find --coverage better), the compilation process generates .gcno files. But then I'm stuck. The command
make NightlyCoverage
doesn't seem to do anything. Could anybody tell me what is the next to do? The result that I want, is by doing make NightlyCoverage, coverage reports are generated and uploaded to cdash server.

I've been using https://github.com/bilke/cmake-modules/blob/master/CodeCoverage.cmake successfully.
Just followed the guidelines: added the files to my CMAKE_MODULE_PATH directory, added
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules)
if(CMAKE_COMPILER_IS_GNUCXX)
include(CodeCoverage)
setup_target_for_coverage(${PROJECT_NAME}_coverage ${PROJECT_TEST_NAME} coverage)
endif()
in my CMakeLists.txt. I also added manually gcov as a dependency for my target:
if(CMAKE_COMPILER_IS_GNUCXX)
target_link_libraries(${PROJECT_TEST_NAME} gcov)
endif()
With this, I just type
make my_project_coverage
and I get the html report in the coverage directory of my build tree.

I use gcovr to make a GCC Code Coverage Report without the CodeCoverage.cmake :
$ cd /path/to/your/project
$ mkdir build && cd build && cmake ..
$ make && make test
$ gcovr -r ../ .

I set up my project 'foo' in the following way. Copied the cmake file from the https://github.com/bilke/cmake-modules/blob/master/CodeCoverage.cmake to a subdirectory 'cmake_modules'. In the CMakeLists.txt file after the add_executable(foo ...) I added the following:
if(CMAKE_COMPILER_IS_GNUCXX)
LIST(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake_modules")
include(CodeCoverage)
APPEND_COVERAGE_COMPILER_FLAGS()
set(COVERAGE_LCOV_EXCLUDES 'dir1/*' 'dir2/*') // this is optional if you want to exclude some directory from the report
SETUP_TARGET_FOR_COVERAGE_LCOV(NAME foo_coverage
EXECUTABLE foo
DEPENDENCIES foo)
endif()
After cmake, build the target
make
make foo_coverage
And open the report with index.html file in the foo_coverage folder in the build folder

This answer is the same as #rcomblen's answer but its a bit outdated so I'll share my solution. Here's what I did:
created a toy project
(base) ciaran#DESKTOP-K0APGUV:/mnt/d/CoverageTest$ tree -L 2
.
├── CMakeLists.txt
├── cmake
│   └── CodeCoverage.cmake
├── src
│   ├── domath.cpp
│   ├── domath.h
│   └── testdomath.cpp
└── third_party
└── googletest
Where
// src/domath.h
#ifndef COVERAGETEST_DOMATH_H
#define COVERAGETEST_DOMATH_H
class domath {
public:
int add(int a, int b);
};
#endif //COVERAGETEST_DOMATH_H
and
// src/domath.cpp
#include "domath.h"
int domath::add(int a, int b) {
return a + b;
}
and
// src/testdomath.cpp
#include "gtest/gtest.h"
#include "domath.h"
TEST(DoMathTests, testAdd){
domath m;
int actual = m.add(4, 6);
ASSERT_EQ(10, actual);
}
download googletest and put it under third party directory
copy the gist so kindly shared by the other answers on this thread into cmake/CodeCoverage.cmake
install gcovr. This step is vital, because the other answers on this thread no longer work with the version of gcovr that I already had:
(base) ciaran#DESKTOP-K0APGUV:/mnt/d/CoverageTest$ pip install gcovr
(base) ciaran#DESKTOP-K0APGUV:/mnt/d/CoverageTest$ gcovr --version
gcovr 4.2
(base) ciaran#DESKTOP-K0APGUV:/mnt/d/CoverageTest$ which gcovr
/home/ciaran/miniconda3/bin/gcovr
Note, we need the output of which gcovr for the cmake script.
4) Write a cmake script that create a library, a test executable and use the CodeCoverage.cmake module:
cmake_minimum_required(VERSION 3.15)
project(CoverageTest)
set(CMAKE_CXX_STANDARD 14)
# setup googletest
add_subdirectory(third_party/googletest)
# create our library
add_library(DoMath STATIC src/domath.h src/domath.cpp)
add_dependencies(DoMath gtest gtest_main)
# create the test executable
add_executable(TestDoMath src/testdomath.cpp)
target_include_directories(TestDoMath PRIVATE third_party/googletest/googletest)
target_link_libraries(TestDoMath PRIVATE
DoMath gtest gtest_main)
add_dependencies(TestDoMath DoMath gtest gtest_main)
# now for coverage bits
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
if (CMAKE_COMPILER_IS_GNUCXX)
include(CodeCoverage)
append_coverage_compiler_flags()
# we need to turn off optimization for non-skewed coverage reports
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0")
# optional excludes - None needed here
# set(COVERAGE_EXCLUDES)
# importantly, set the path to the gcovr executable that you downladed
set(GCOVR_PATH "/home/ciaran/miniconda3/bin/gcovr")
# Works
setup_target_for_coverage_gcovr_xml(
NAME TestDoMathCoverageXml
EXECUTABLE TestDoMath
DEPENDENCIES TestDoMath DoMath
)
# Works
setup_target_for_coverage_gcovr_html(
NAME TestDoMathCoverageHtml
EXECUTABLE TestDoMath
DEPENDENCIES TestDoMath DoMath
)
# This one did not work for me:
setup_target_for_coverage_lcov(
NAME TestDoMathCoverageLcov
EXECUTABLE TestDoMath
DEPENDENCIES TestDoMath DoMath
)
endif ()
And thats it. Now just build the new targets.
Good luck.

Related

Why CTest can't find tests with googletest ? Also, can I run a single test function by mouse click in visual studio code like in visual studio? [duplicate]

I have a project with a structure
├── CMakeLists.txt
├── mzl.c
├── mzl.h
└── tests
├── CMakeLists.txt
├── mzl-communication-test.c
├── mzl-setup-test.c
├── mzl-test-errors.c
└── mzl-test-errors.h
Where the top directory CMakeLists.txt file is
project(mzl)
cmake_minimum_required(VERSION 2.8)
add_subdirectory(tests)
# Enable testing for the project
enable_testing()
# Find zmq
find_library(ZMQ_LIB zmq REQUIRED)
message(STATUS "ZMQ Library: ${ZMQ_LIB}")
# Find threading library
set(CMAKE_THREAD_PREFER_PTHREAD ON)
find_package(Threads REQUIRED)
message(STATUS "Threading Library: ${CMAKE_THREAD_LIBS_INIT}")
# Set include directories for headers
set (
MZL_INCLUDE_DIRS
${CMAKE_SOURCE_DIR}
CACHE STRING "MZL Include Directories"
)
include_directories(${MZL_INCLUDE_DIRS})
# Set source files
set (
MZL_SRC_FILES
mzl.c
)
# Add library
add_library(${PROJECT_NAME} STATIC ${MZL_SRC_FILES})
# Link to zmq
target_link_libraries(${PROJECT_NAME} ${ZMQ_LIB} ${CMAKE_THREAD_LIBS_INIT})
and tests/CMakeLists.txt is
# Use MZL Source Directories (mzl headers are in top directory)
include_directories(${CMAKE_SOURCE_DIR})
# Variable from here is empty
message(STATUS "MZL Include Directories: ${MZL_INCLUDE_DIRS}")
# Files common to all tests
set ( TEST_COMMON_SOURCES
mzl-test-errors.c
)
# Library setup/shutdown testing
set(SETUP_TEST_NAME mzl-setup-test)
add_executable(${SETUP_TEST_NAME} ${TEST_COMMON_SOURCES} ${SETUP_TEST_NAME}.c)
target_link_libraries(${SETUP_TEST_NAME} ${PROJECT_NAME} ${ZMQ_LIB})
add_test(${SETUP_TEST_NAME} ${SETUP_TEST_NAME})
# Communcations test
set(COMMUNICATION_TEST_NAME mzl-communication-test)
add_executable(${COMMUNICATION_TEST_NAME} ${TEST_COMMON_SOURCES}
${COMMUNICATION_TEST_NAME}.c)
target_link_libraries(${COMMUNICATION_TEST_NAME} ${PROJECT_NAME}
${CMAKE_THREAD_LIBS_INIT} ${ZMQ_LIB})
add_test(${COMMUNICATION_TEST_NAME} ${COMMUNICATION_TEST_NAME})
Everything worked fine before I added the second test mzl-communication-test. After adding it and the lines in the tests/CMakeLists.txt after # Communications test, running ctest did nothing extra -- it still only ran the first test.
After deleting the build directory and running cmake again, I get no errors for the initial CMake run, but running make runs CMake again, resulting in an error with CMake:
CMake Error: Parse error in cache file build/CMakeCache.txt. Offending entry: include
followed by a Make error:
Makefile:203: recipe for target 'cmake_check_build_system' failed
make: *** [cmake_check_build_system] Error 1
Running make again results in everything being built, including the tests; however, running ctest results in
Test project build
No tests were found!!!
The issue seems to be with something that I am doing with CMake, but can't figure out what to do from here as I can't see anything that I'm doing differently than when it was originally working. Even my last commit to git, which was working when I committed it, is no longer working.
You need to move the enable_testing() call to be before you do add_subdirectory(tests)
# Enable testing for the project
enable_testing()
add_subdirectory(tests)

CMake ExternalProject does not unpack dependencies

I am building an application which uses tinyxml2 and few other dependencies (namely Irrlicht and IrrKlang) that I provide as .zip files in the Dependency subdirectory of my project:
.
├── CMakeLists.txt
├── Dependencies
│   ├── irrKlang-1.5.0.zip
│   ├── irrKlang-32bit-1.5.0.zip
│   ├── irrKlang-64bit-1.5.0.zip
│   ├── irrlicht-1.8.4.zip
│   └── tinyxml2-master.zip
├── Editor
│   ├── CMakeLists.txt
│   └── Sources
│   └── main.cpp
└── Game
   ├── CMakeLists.txt
   └── Sources
   └── main.cpp
NOTE: for a reference, full sources are available on GitHub, here I cut some corners to make the question shorter.
The top-level CMakeFiles.txt is set up is:
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
project(shoot-them VERSION 1.0.1 LANGUAGES CXX)
include(ExternalProject)
# few platform-specific variables like MAKE_COMMAND, PLATFORM_ARCH, etc.
# libraries (see below)
add_subdirectory(Game)
add_subdirectory(Editor
Both Irrlicht and IrrKlang come with pre-built libraries for Windows for x86, but not for Windows x64 and not for OSX. Hence I add it as a dependency like this (using the if(NOT IRRLICHT_LIBRARY_PATH) just to separate the code into a block:
if(NOT IRRLICHT_LIBRARY_PATH)
ExternalProject_Add(irrlicht-dep
URL ${CMAKE_CURRENT_LIST_DIR}/Dependencies/irrlicht-1.8.4.zip
PREFIX Dependencies/irrlicht
SOURCE_SUBDIR source/Irrlicht
CONFIGURE_COMMAND ""
BUILD_COMMAND "${MAKE_COMMAND}"
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(irrlicht-dep SOURCE_DIR)
set(IRRLICHT_PATH ${SOURCE_DIR})
add_library(irrlicht SHARED IMPORTED GLOBAL)
set_target_properties(
irrlicht PROPERTIES
IMPORTED_LOCATION ${IRRLICHT_PATH}/lib/${IRRLICHT_PATH_SUFFIX}/Irrlicht${CMAKE_LINK_LIBRARY_SUFFIX}
IMPORTED_IMPLIB ${IRRLICHT_PATH}/lib/${IRRLICHT_PATH_SUFFIX}/Irrlicht${CMAKE_IMPORT_LIBRARY_SUFFIX}
INTERFACE_INCLUDE_DIRECTORIES ${IRRLICHT_PATH}/include
)
endif()
I follow the same principles for IrrKlang. But since tinyxml2 comes as a header and a source file and it comes packed with CMakeLists.txt, I just include it like this:
ExternalProject_Add(tinyxml2-dep
URL ${CMAKE_CURRENT_LIST_DIR}/Dependencies/tinyxml2-master.zip
PREFIX Dependencies/tinyxml2
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(tinyxml2-dep SOURCE_DIR)
set(TINYXML2_PATH ${SOURCE_DIR})
add_subdirectory(${TINYXML2_PATH})
I define both Game and Editor sub-projects as follows:
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
project(Editor VERSION 1.0.1 LANGUAGES CXX)
set(EXECUTABLE_NAME Editor)
set(SOURCES Sources/main.cpp)
# platform-specific variables
set_target_properties(${EXECUTABLE_NAME} PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS ON
)
add_dependencies(${EXECUTABLE_NAME} tinyxml2 irrlicht)
target_link_libraries(${EXECUTABLE_NAME} PUBLIC ${LIBRARIES} tinyxml2 irrlicht)
if(NOT APPLE)
# Copy libraries' DLLs
add_custom_command(
TARGET ${EXECUTABLE_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:tinyxml2> $<TARGET_FILE_DIR:${EXECUTABLE_NAME}>
)
endif()
# TODO: copy DLLs, not LIBs
add_custom_command(
TARGET ${EXECUTABLE_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:irrlicht> $<TARGET_FILE_DIR:${EXECUTABLE_NAME}>
)
The way I use ExternalProject specifically is because
I wanted to fix the versions I build my project with
I did not want to pollute my system with installing the libraries (hence no INSTALL steps, although I may completely misunderstand that concept)
I do not depend on 3rd party repositories being dead (hence shipping the ZIP files with the sources)
I do not rely on unbelievably outdated (and thus potentially non-working) find modules for CMake
To be fair: I am not aware of any best practices of building projects with CMake, so I might very well be completely wrong about all of the above, so please do correct me.
When I build this project in Visual Studio 2019 on Windows, it works like a charm. But whenever I try building the thing on OSX, I get failures:
none of the dependencies gets even unpacked
(because of p.1) the ${TINYXML2_DIR} is never set
(because of p.2) the tinyxml2 directory could not be found and thus added via add_subdirectory()
(because of p.3) the $<TARGET_FILE:tinyxml2> expression does not evaluate
(as a global consequence) the project does not build
The way I build project is rather simple:
cmake -Bbuild -H. && cmake --build build
What am I doing wrong?
Also, what is the right way to handle 3rd party dependencies with CMake?
I am very well aware that CMake is technically just a makefile (roughly speaking, since it is different for every build toolchain) generator, so my question is more about how do I tell CMake to generate the correct build files for each type of dependency that should be built with my project (pre-built, build from sources with CMake, build from sources with a custom command). I thought ExternalProject is supposed to handle just that, but apparently something went horribly wrong along the way.
I have played with both a solution suggested by #Mizux in their comment and had some success with two different approaches.
1. vcpkg
This is arguably the easier of the two. It requires vcpkg installed.
See this commit for example.
Create a manifest file, vcpkg.json in the project root directory, listing all the dependencies used by the project:
{
"name": "PROJECT_NAME",
"version-string": "0.1.0",
"dependencies": [
"irrlicht",
"tinyxml2"
]
}
You can also use CLI to generate the manifest by using vcpkg install command.
Use the find_package from CMake to link libraries to each target - in the child CMakeLists.txt:
find_package(irrlicht CONFIG REQUIRED)
find_package(tinyxml2 CONFIG REQUIRED)
target_link_libraries(${EXECUTABLE_NAME} PUBLIC ${LIBRARIES} tinyxml2::tinyxml2 Irrlicht)
Important: when configuring the project, pass the path to the vcpkg CMake module:
cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=[vcpkg root]/scripts/buildsystems/vcpkg.cmake
2. FetchContent + ExternalProject
Since my project depends on both a library that ships with CMakeLists.txt and a library that does not, this is a nice example.
See this commit for example.
First, as #Mizux mentioned, FetchContent works at configure time - it downloads and unpacks the dependency when you configure the project (call cmake -S . -B build). Then, since irrlicht does not ship with CMakeLists.txt, you either use ExternalProject_Add to build it with custom command (make in my case) or add it as a sub-directory to the project.
FetchContent part:
include(FetchContent)
FetchContent_Declare(irrlicht
URL ${CMAKE_CURRENT_LIST_DIR}/Dependencies/irrlicht-1.8.4.zip
)
FetchContent_GetProperties(irrlicht)
if(NOT irrlicht_POPULATED)
FetchContent_Populate(irrlicht)
endif()
set(IRRLICHT_PATH ${irrlicht_SOURCE_DIR})
ExternalProject part:
include(ExternalProject)
if(NOT WIN32)
ExternalProject_Add(irrlicht-dep
SOURCE_DIR "${IRRLICHT_PATH}/source/Irrlicht"
BUILD_IN_SOURCE true
CONFIGURE_COMMAND ""
BUILD_COMMAND "${MAKE_COMMAND}"
)
endif()
add_library(irrlicht SHARED IMPORTED GLOBAL)
set_target_properties(
irrlicht PROPERTIES
IMPORTED_LOCATION ${IRRLICHT_PATH}/lib/${IRRLICHT_PATH_SUFFIX}/Irrlicht${CMAKE_LINK_LIBRARY_SUFFIX}
IMPORTED_IMPLIB ${IRRLICHT_PATH}/lib/${IRRLICHT_PATH_SUFFIX}/Irrlicht${CMAKE_IMPORT_LIBRARY_SUFFIX}
INTERFACE_INCLUDE_DIRECTORIES ${IRRLICHT_PATH}/include
)
Note the important bits in the ExternalProject configuration:
irrlicht_SOURCE_DIR variable is populated by FetchContent
SOURCE_DIR "${irrlicht_SOURCE_DIR}/source/Irrlicht" - tells ExternalProject where the unpacked sources are; if this directory is not empty (which it should be not, since FetchContent should have populated it at project configuration time), ExternalProject will skip the download phase
BUILD_IN_SOURCE true - builds from within the source directory
CONFIGURE_COMMAND "" - skips the dependency configuration phase, so not cmake will be executed for the dependency
BUILD_COMMAND "make" - uses a specific command to build the dependency from sources
if(NOT WIN32) - only uses ExternalProject to build the dependency; since Irrlicht comes with pre-built libraries for Win32, this should build the thing for the other platforms (including Win64)

Error running google test on default in CLion

So, unfortunatly I am unsure how to properly describe the error message.
Essentially I am trying to get used to google test, - I want to use it to test my C++ project in CLion. I create a new library project, with the following classes:
#include "gtest/gtest.h"
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
and also:
#include "gtest/gtest.h"
TEST(MyTestCategory, Vec2DAdditionTest){
EXPECT_EQ(1, 1);
}
Of course these tests are not useful at all - but its just to see if everything works the way it should.
Now when I tried to run them, I am prompted the following error:
6:46 PM Error running 'MyTestCategory.Vec2DAdditionTest': Cannot run 'MyTestCategory.Vec2DAdditionTest' on '<default>'
6:47 PM Error running 'All in main.cpp': Cannot run 'All in main.cpp' on '<default>'
What am I missing? I can't get the tests to run, - neither individually, nor directly over the main function?
Also after this, the build/run button gets greyed out in CLion and I have to right click on the main.cpp to force it to run/compile..
Essential I have a project structure like so:
src/
a.cpp
b.cpp
CMakeLists.txt
test/
main.cpp
atests.cpp
CmakeLists.txt
CMakeLists.txt
My run configuration for the test project looks like so:
Here is an example on how you can add GTests in your CLion project:
Consider a project structure very similar to what you have presented, however, with an additional file CMakeLists.txt.in in the test folder.:
src/
a.cpp
b.cpp
CMakeLists.txt
test/
main.cpp
atests.cpp
CMakeLists.txt
CMakeLists.txt.in
CMakeLists.txt
The CMakeLists.txt.in helps to download and add the GTest libraries to your project while building your project.
The content of CMakeLists.txt.in looks as below:
cmake_minimum_required(VERSION 2.8.2)
project(googletest-download NONE)
include(ExternalProject)
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG master
SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src"
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)
The test/CMakeLists.txt file looks as below:
cmake_minimum_required(VERSION 3.10)
### START OF CONFIGURING GTEST
configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download )
execute_process(COMMAND ${CMAKE_COMMAND} --build .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download )
# Add googletest directly to our build. This defines
# the gtest and gtest_main targets.
add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src
${CMAKE_CURRENT_BINARY_DIR}/googletest-build
EXCLUDE_FROM_ALL)
### END OF CONFIGURING GTEST
# Now simply link against gtest or gtest_main as needed. Eg
add_executable(test_${PROJECT_NAME} main.cpp atest.cpp)
target_link_libraries(test_${PROJECT_NAME} ${PROJECT_NAME} gtest gtest_main)
add_test(NAME test_PROJECT_NAME COMMAND test_${PROJECT_NAME})
CMakeLists.txt in the root folder has the following content:
cmake_minimum_required(VERSION 3.10)
project(gtestTest)
set(CMAKE_CXX_STANDARD 14)
add_subdirectory(src)
add_subdirectory(test)
Now reload the CMake configuration and try running the test_gtestTest target to run the unit tests.
You can also create custom run configurations using gtest template of CLion to get user friendly test reports.
For more information on GTests with CLion, please refer to:
Google Test Support in Clion
Building Google Tests with CMake

Unable to link classes in GTest with CMake

I am trying to incorporate GTests in my project using CMake. I followed this instruction and I get following error:
Undefined symbols for architecture x86_64:
"SomeClass::someMethod()", referenced from:
someMethod_TrivialTest_Test::TestBody() in SomeClassTest.cpp.o
ld: symbol(s) not found for architecture x86_64
Here is my CMake file:
cmake_minimum_required(VERSION 3.11)
project (someProject)
set(CMAKE_CXX_STANDARD 17)
#Include headers
include_directories("../include")
#Add source files
file(GLOB SOURCES "src/*.cpp")
add_executable(someProject ${SOURCES})
################################
# GTest
################################
# Download and unpack googletest at configure time
configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
if(result)
message(FATAL_ERROR "CMake step for googletest failed: ${result}")
endif()
execute_process(COMMAND ${CMAKE_COMMAND} --build .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
if(result)
message(FATAL_ERROR "Build step for googletest failed: ${result}")
endif()
# Prevent overriding the parent project's compiler/linker
# settings on Windows
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# Add googletest directly to our build. This defines
# the gtest and gtest_main targets.
add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src
${CMAKE_BINARY_DIR}/googletest-build
EXCLUDE_FROM_ALL)
# The gtest/gtest_main targets carry header search path
# dependencies automatically when using CMake 2.8.11 or
# later. Otherwise we have to add them here ourselves.
if (CMAKE_VERSION VERSION_LESS 2.8.11)
include_directories("${gtest_SOURCE_DIR}/include")
endif()
# Add test cpp file
add_executable( someTests ./test/SomeClassTest.cpp)
# Link test executable against gtest & gtest_main
target_link_libraries(someTests gtest gtest_main)
add_test( someTests someTests )
My project structure (CMakeLists.txt.in is used to download the GTest library, added from the instruction mentioned above):
.
├── CMakeLists.txt
├── CMakeLists.txt.in
├── include
│ └──SomeClass.hpp
├── src
│ ├── Main.cpp
│ └── SomeClass.cpp
└── tests
└── SomeClassTest.cpp
If I manually create and link objects, it works.
g++ -c ./src/SomeClass.cpp
g++ -c ./test/SomeClassTest.cpp
g++ SomeClassTest.o SomeClass.o ./build/googletest-build/googlemock/gtest/CMakeFiles/gtest.dir/src/gtest-all.cc.o -o myTests
The object of the test class seems to be there:
./build/CMakeFiles/someTests.dir/test/SomeClassTest.cpp.o
But the directory for the someProject object is empty:
./build/CMakeFiles/someProject.dir/src/
So I am missing something in the CMake file and it is not linking properly. All the answers I could find so far were related to specifying target_link_libraries but I am not sure if that is the problem here. I am new to CMake and linking issues. Would reeeally appreciate any advice!
All the answers I could find so far were related to specifying target_link_libraries but I am not sure if that is the problem here.
You were right on track with this assumption.
Currently, you're telling CMake that there is an executable to be built from some sources and a test to be built from some other sources and linked with gtest. CMake doesn't know that the executable and your test are in any way related, though!
I see two possible solutions:
add the sources of the executable (except the one with the main function) as sources to the test.
build all of the sources from the executable (except the one with the main function) as library and link both the executable and the test to that library.
I prefer the later one, which would - roughly - look like:
add_library(someProgram_lib ${SOURCES})
add_executable(someProgram src/main.cc)
target_link_libraries(someProgram someProgram_lib)
add_executable(someTests ./test/SomeClassTest.cpp)
target_link_libraries(someTests gtest gtest_main someProgram_lib)

How to build gtest static library from my project CMakeLists.txt?

I have a main project with a subproject tests.
In project/CMakeLists.txt I added
add_subdirectory(tests)
In project/tests/CMakeLists.txt I plug in "pre-compiled" gtest library (from Ubuntu repository). I cd to /usr/src/gtest and compile two *.a files into system lib directory.
And it works fine, until issue scribed in google test FAQ appeared.
How to build gtest static library files from project/tests/CMakeLists.txt and how to use this new *.a files instead of system ones?
Finaly I find out how to rebuild static libgtest.a without adding googletest as a subproject.
Using info from this link and from the bottom of this link.
Interesting part of my project/tests/CMakeLists.txt:
.....
# Locate GTest
#find_package(GTest REQUIRED)
#include_directories(SYSTEM ${GTEST_INCLUDE_DIRS})
set(GTEST_SRC /usr/src/gtest/src/gtest-all.cc)
include_directories(SYSTEM /usr/src/gtest # <-- path to non-header files from gtest-all.cc
/usr/include) # <-- path to gtest headers
add_library(gtest STATIC ${GTEST_SRC}) # <-- make static library target before main project executable
target_link_libraries(gtest ${CMAKE_THREAD_LIBS_INIT})
# Link runTests with what we want to test and the GTest and pthread library
add_executable(tests ${SRC})
target_link_libraries(tests ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} gtest) # <-- finaly link this library as others
I'm starting from beginning.
First, download the googletest source code from
https://github.com/google/googletest
When we download this ZIP-files, we get the googletest and googlemock project, but here I will only use the googletest folder. I unpack the ZIP and copy the googletest folder to my project, e.g.:
/home/name/myProject/
The content from this folder could look like the following:
- CMakeLists.txt
- googletest /
- main.cpp
In the CMakeList file I can add gtest as a subdir and link my executable against it. I build a minimal example:
cmake_minimum_required(VERSION 2.6.4)
project(MyProject)
enable_testing()
add_subdirectory( googletest )
# this sets the output dir to /bin
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
include_directories(${CMAKE_SOURCE_DIR}/googletest/include)
add_executable(myExe main.cpp)
target_link_libraries(myExe gtest_main gtest)
The last three lines set the include path to googletest, add the executable and link the exe against gtest.
After downloading gtest and creating the cmake structure, you can build you program:
$ mkdir bin
$ cd bin
$ cmake ..
$ make
and call the exe:
$ ./myExe
I hope this will help.