Search code examples
c++makefilecmakegoogletest

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!


Solution

  • 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)