使用 cmake 构建 google 测试单元测试时出现链接器错误

linker error when building google test unit test using cmake

如何获取要构建的 cmake / gtest 代码

我在 lib 文件夹中有库源代码:

calculations.hpp:

#ifndef  CALCULATIONS_HPP_
#define CALCULATIONS_HPP_

class calculations {
public:
  int square(int n);
};

#endif // CALCULATIONS_HPP_

calculations.cpp:

#include "calculations.hpp"

int calculations::square(int n) {
  return n * n;
}

CMakeLists.txt 此文件夹中的文件:

set(SRC_FILES calculations.cpp)
add_library(Calculations STATIC "${SRC_FILES}")

在 'root' 文件夹中:

calculations_unittests.cpp:

#include "calculations.hpp"

#include <gtest/gtest.h>

TEST(CalculationsUnitTests, square_test) {
  calculations calc;
  ASSERT_EQ(calc.square(2), 4);
}

int main(int argc, char **argv) {
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

CMakeLists.txt 在此根文件夹中:

cmake_minimum_required(VERSION 3.5)

set (CMAKE_CXX_STANDARD 14)

set(GTEST_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libGoogleTest/googletest/include/")

message( "GTEST_INCLUDE_DIR = ${GTEST_INCLUDE_DIR}" )

message( "CMAKE_CURRENT_SOURCE_DIR = ${CMAKE_CURRENT_SOURCE_DIR}" )

include_directories("${GTEST_INCLUDE_DIR}"
                    "${CMAKE_CURRENT_SOURCE_DIR}/lib/")

set(GTEST_LIBRARY "${CMAKE_CURRENT_SOURCE_DIR}/libGoogleTest/googletest/libgtest.a")
set(GTEST_MAIN_LIBRARY "${CMAKE_CURRENT_SOURCE_DIR}/libGoogleTest/googletest/libgtest_main.a")

message( "GTEST_LIBRARY = ${GTEST_LIBRARY}" )


add_subdirectory(lib)

# Locate GTest
find_package(GTest REQUIRED)

message( "lib = ${Calculations}" )

# Link runTests with what we want to test and the GTest and pthread library
add_executable(runTests calculations_unittests.cpp ${Calculations})

此根文件夹中还有 gtest 文件夹结构。带有文件夹的 libGoogleTest 文件夹。

我还构建了 libgtest.a 和 libgtest_main.a lib 文件,它们位于 libGoogleTest/googletest 文件夹中。

如果我 运行 来自 'root' 文件夹的以下命令:

combera@combera-1604_64:~/Documents/scratch/cmake_gtest$ cmake CMakeLists.txt 
GTEST_INCLUDE_DIR = /home/combera/Documents/scratch/cmake_gtest/libGoogleTest/googletest/include/
CMAKE_CURRENT_SOURCE_DIR = /home/combera/Documents/scratch/cmake_gtest
GTEST_LIBRARY = /home/combera/Documents/scratch/cmake_gtest/libGoogleTest/googletest/libgtest.a
lib = 
-- Configuring done
-- Generating done
-- Build files have been written to: /home/combera/Documents/scratch/cmake_gtest
combera@combera-1604_64:~/Documents/scratch/cmake_gtest$ make runTests
[ 50%] Linking CXX executable runTests
CMakeFiles/runTests.dir/calculations_unittests.cpp.o: In function `CalculationsUnitTests_square_test_Test::TestBody()':
calculations_unittests.cpp:(.text+0x30): undefined reference to `calculations::square(int)'
calculations_unittests.cpp:(.text+0x73): undefined reference to `testing::Message::Message()'
calculations_unittests.cpp:(.text+0xa0): undefined reference to `testing::internal::AssertHelper::AssertHelper(testing::TestPartResult::Type, char const*, int, char const*)'
calculations_unittests.cpp:(.text+0xb3): undefined reference to `testing::internal::AssertHelper::operator=(testing::Message const&) const'
calculations_unittests.cpp:(.text+0xbf): undefined reference to `testing::internal::AssertHelper::~AssertHelper()'
calculations_unittests.cpp:(.text+0x107): undefined reference to `testing::internal::AssertHelper::~AssertHelper()'
CMakeFiles/runTests.dir/calculations_unittests.cpp.o: In function `main':
calculations_unittests.cpp:(.text+0x164): undefined reference to `testing::InitGoogleTest(int*, char**)'
CMakeFiles/runTests.dir/calculations_unittests.cpp.o: In function `__static_initialization_and_destruction_0(int, int)':
calculations_unittests.cpp:(.text+0x1da): undefined reference to `testing::internal::GetTestTypeId()'
calculations_unittests.cpp:(.text+0x248): undefined reference to `testing::internal::MakeAndRegisterTestInfo(char const*, char const*, char const*, char const*, testing::internal::CodeLocation, void const*, void (*)(), void (*)(), testing::internal::TestFactoryBase*)'
CMakeFiles/runTests.dir/calculations_unittests.cpp.o: In function `RUN_ALL_TESTS()':
calculations_unittests.cpp:(.text._Z13RUN_ALL_TESTSv[_Z13RUN_ALL_TESTSv]+0x5): undefined reference to `testing::UnitTest::GetInstance()'
calculations_unittests.cpp:(.text._Z13RUN_ALL_TESTSv[_Z13RUN_ALL_TESTSv]+0xd): undefined reference to `testing::UnitTest::Run()'
CMakeFiles/runTests.dir/calculations_unittests.cpp.o: In function `CalculationsUnitTests_square_test_Test::CalculationsUnitTests_square_test_Test()':
calculations_unittests.cpp:(.text._ZN38CalculationsUnitTests_square_test_TestC2Ev[_ZN38CalculationsUnitTests_square_test_TestC5Ev]+0x14): undefined reference to `testing::Test::Test()'
CMakeFiles/runTests.dir/calculations_unittests.cpp.o: In function `testing::internal::scoped_ptr<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::reset(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*)':
calculations_unittests.cpp:(.text._ZN7testing8internal10scoped_ptrINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE5resetEPS7_[_ZN7testing8internal10scoped_ptrINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE5resetEPS7_]+0x29): undefined reference to `testing::internal::IsTrue(bool)'
CMakeFiles/runTests.dir/calculations_unittests.cpp.o: In function `testing::internal::scoped_ptr<std::__cxx11::basic_stringstream<char, std::char_traits<char>, std::allocator<char> > >::reset(std::__cxx11::basic_stringstream<char, std::char_traits<char>, std::allocator<char> >*)':
calculations_unittests.cpp:(.text._ZN7testing8internal10scoped_ptrINSt7__cxx1118basic_stringstreamIcSt11char_traitsIcESaIcEEEE5resetEPS7_[_ZN7testing8internal10scoped_ptrINSt7__cxx1118basic_stringstreamIcSt11char_traitsIcESaIcEEEE5resetEPS7_]+0x28): undefined reference to `testing::internal::IsTrue(bool)'
CMakeFiles/runTests.dir/calculations_unittests.cpp.o: In function `testing::AssertionResult testing::internal::CmpHelperEQ<int, int>(char const*, char const*, int const&, int const&)':
calculations_unittests.cpp:(.text._ZN7testing8internal11CmpHelperEQIiiEENS_15AssertionResultEPKcS4_RKT_RKT0_[_ZN7testing8internal11CmpHelperEQIiiEENS_15AssertionResultEPKcS4_RKT_RKT0_]+0x43): undefined reference to `testing::AssertionSuccess()'
CMakeFiles/runTests.dir/calculations_unittests.cpp.o: In function `testing::AssertionResult testing::internal::CmpHelperEQFailure<int, int>(char const*, char const*, int const&, int const&)':
calculations_unittests.cpp:(.text._ZN7testing8internal18CmpHelperEQFailureIiiEENS_15AssertionResultEPKcS4_RKT_RKT0_[_ZN7testing8internal18CmpHelperEQFailureIiiEENS_15AssertionResultEPKcS4_RKT_RKT0_]+0x87): undefined reference to `testing::internal::EqFailure(char const*, char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool)'
CMakeFiles/runTests.dir/calculations_unittests.cpp.o:(.rodata._ZTV38CalculationsUnitTests_square_test_Test[_ZTV38CalculationsUnitTests_square_test_Test]+0x20): undefined reference to `testing::Test::SetUp()'
CMakeFiles/runTests.dir/calculations_unittests.cpp.o:(.rodata._ZTV38CalculationsUnitTests_square_test_Test[_ZTV38CalculationsUnitTests_square_test_Test]+0x28): undefined reference to `testing::Test::TearDown()'
CMakeFiles/runTests.dir/calculations_unittests.cpp.o: In function `CalculationsUnitTests_square_test_Test::~CalculationsUnitTests_square_test_Test()':
calculations_unittests.cpp:(.text._ZN38CalculationsUnitTests_square_test_TestD2Ev[_ZN38CalculationsUnitTests_square_test_TestD5Ev]+0x20): undefined reference to `testing::Test::~Test()'
CMakeFiles/runTests.dir/calculations_unittests.cpp.o:(.rodata._ZTI38CalculationsUnitTests_square_test_Test[_ZTI38CalculationsUnitTests_square_test_Test]+0x10): undefined reference to `typeinfo for testing::Test'
collect2: error: ld returned 1 exit status
CMakeFiles/runTests.dir/build.make:94: recipe for target 'runTests' failed
make[3]: *** [runTests] Error 1
CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/runTests.dir/all' failed
make[2]: *** [CMakeFiles/runTests.dir/all] Error 2
CMakeFiles/Makefile2:79: recipe for target 'CMakeFiles/runTests.dir/rule' failed
make[1]: *** [CMakeFiles/runTests.dir/rule] Error 2
Makefile:118: recipe for target 'runTests' failed
make: *** [runTests] Error 2

我在任何地方都没有看到任何 libCalculations.a 创建。

如何解决这个问题?

我可以像这样使用 makefile 进行构建:

LIB_DIR=./lib

GOOGLE_TEST_LIB = gtest

GOOGLE_TEST_INCLUDE=./libGoogleTest/googletest/include/

G++ = g++
G++_FLAGS = -c -Wall -I $(GOOGLE_TEST_INCLUDE) -I ./lib
LD_FLAGS = -L ./libGoogleTest/googletest -l $(GOOGLE_TEST_LIB) -l pthread libCalculations.a

OBJECTS = main.o
TARGET = main

all: $(TARGET)

libCalculations.a: libCalculations.a
    ar rcs libCalculations.a calculations.o

calculations.o: calculations.hpp calculations.cpp
    g++ -o calculations.o $(LIB_DIR)/calculations.cpp

$(TARGET): $(OBJECTS)
    g++ -o $(TARGET) $(OBJECTS) $(LD_FLAGS)

%.o : %.cpp
    $(G++) $(G++_FLAGS) $<

clean:
    rm -f $(TARGET) $(OBJECTS)

.PHONY: all clean

但是我如何使用 cmake CMakeLists.txt 做同样的事情?

经过几个小时的试验,我发现这个 CMakeLists.txt 文件有效:

cmake_minimum_required(VERSION 3.5)

project(calcTests)

set (CMAKE_CXX_STANDARD 14)

set(GTEST_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libGoogleTest/googletest/include/")

#message( "GTEST_INCLUDE_DIR = ${GTEST_INCLUDE_DIR}" )

#message( "CMAKE_CURRENT_SOURCE_DIR = ${CMAKE_CURRENT_SOURCE_DIR}" )

include_directories("${GTEST_INCLUDE_DIR}"
                    "${CMAKE_CURRENT_SOURCE_DIR}/lib/")

set(GTEST_LIBRARY "${CMAKE_CURRENT_SOURCE_DIR}/libGoogleTest/googletest/libgtest.a")
set(GTEST_MAIN_LIBRARY "${CMAKE_CURRENT_SOURCE_DIR}/libGoogleTest/googletest/libgtest_main.a")

message( "GTEST_LIBRARY: ${GTEST_LIBRARY}" )


add_subdirectory(lib)

# it seems that gtest is built with the shared .so pthreads lib
find_library(pthreads NAMES libpthread.so FIND_LIBRARY_USE_LIB64_PATHS)
message( "Using thread lib: ${pthreads}" )

# Locate GTest
find_package(GTest REQUIRED)

set(CMAKE_PREFIX_PATH "./lib")
find_library(Calculations NAMES libCalculations.a "./lib/")

message( "Calculations lib: ${Calculations}" )


add_executable(calcTests calculations_unittests.cpp ${Calculations} ${pthreads})

target_link_libraries(calcTests ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY} ${Calculations} ${pthreads})