Issue
I have a tests for my project written with gtest. I use CMake for compiling project, and it works without issues with gcc:
cmake_minimum_required(VERSION 3.26)
project(cpp_stream_socket_tests)
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip
)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
enable_testing()
file(GLOB SRCS "src/*.cpp")
file(GLOB HEADERS "include/*.hpp")
add_executable(${PROJECT_NAME})
target_sources(${PROJECT_NAME} PRIVATE
${SRCS}
PRIVATE FILE_SET HEADERS BASE_DIRS include FILES ${HEADERS}
)
set_target_properties(${PROJECT_NAME} PROPERTIES
LINKER_LANGUAGE CXX
CXX_STANDARD 23
CXX_STANDARD_REQUIRED TRUE
)
target_link_libraries(${PROJECT_NAME} gtest_main cpp_stream_socket)
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(${PROJECT_NAME} PUBLIC -stdlib=libc++)
endif()
add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME})
However, when I try to compile tests using clang, I get linker errors:
/usr/bin/ld: CMakeFiles/cpp_stream_socket_tests.dir/src/basic_operations.cpp.o: in function `void posix::net::tests::detail::check_res<std::__1::reference_wrapper<posix::net::socket<(posix::net::side)1, (posix::net::domain)2, (posix::net::type)1, posix::net::ipv4_address, posix::net::policy::multithread> > >(std::__1::expected<std::__1::reference_wrapper<posix::net::socket<(posix::net::side)1, (posix::net::domain)2, (posix::net::type)1, posix::net::ipv4_address, posix::net::policy::multithread> >, posix::net::error> const&)':
basic_operations.cpp:(.text._ZN5posix3net5tests6detail9check_resINSt3__117reference_wrapperINS0_6socketILNS0_4sideE1ELNS0_6domainE2ELNS0_4typeE1ENS0_12ipv4_addressENS0_6policy11multithreadEEEEEEEvRKNS4_8expectedIT_NS0_5errorEEE[_ZN5posix3net5tests6detail9check_resINSt3__117reference_wrapperINS0_6socketILNS0_4sideE1ELNS0_6domainE2ELNS0_4typeE1ENS0_12ipv4_addressENS0_6policy11multithreadEEEEEEEvRKNS4_8expectedIT_NS0_5errorEEE]+0x11f): undefined reference to `testing::internal::GetBoolAssertionFailureMessage(testing::AssertionResult const&, char const*, char const*, char const*)'
/usr/bin/ld: CMakeFiles/cpp_stream_socket_tests.dir/src/basic_operations.cpp.o: in function `testing::AssertionResult testing::internal::CmpHelperEQFailure<posix::net::state, posix::net::state>(char const*, char const*, posix::net::state const&, posix::net::state const&)':
basic_operations.cpp:(.text._ZN7testing8internal18CmpHelperEQFailureIN5posix3net5stateES4_EENS_15AssertionResultEPKcS7_RKT_RKT0_[_ZN7testing8internal18CmpHelperEQFailureIN5posix3net5stateES4_EENS_15AssertionResultEPKcS7_RKT_RKT0_]+0x7f): undefined reference to `testing::internal::EqFailure(char const*, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool)'
/usr/bin/ld: CMakeFiles/cpp_stream_socket_tests.dir/src/basic_operations.cpp.o: in function `void testing::internal::RawBytesPrinter::PrintValue<posix::net::state, 4ul>(posix::net::state const&, std::__1::basic_ostream<char, std::__1::char_traits<char> >*)':
basic_operations.cpp:(.text._ZN7testing8internal15RawBytesPrinter10PrintValueIN5posix3net5stateELm4EEEvRKT_PNSt3__113basic_ostreamIcNS9_11char_traitsIcEEEE[_ZN7testing8internal15RawBytesPrinter10PrintValueIN5posix3net5stateELm4EEEvRKT_PNSt3__113basic_ostreamIcNS9_11char_traitsIcEEEE]+0x1e): undefined reference to `testing::internal::PrintBytesInObjectTo(unsigned char const*, unsigned long, std::__1::basic_ostream<char, std::__1::char_traits<char> >*)'
/usr/bin/ld: CMakeFiles/cpp_stream_socket_tests.dir/src/basic_operations.cpp.o: in function `testing::AssertionResult::AppendMessage(testing::Message const&)':
basic_operations.cpp:(.text._ZN7testing15AssertionResult13AppendMessageERKNS_7MessageE[_ZN7testing15AssertionResult13AppendMessageERKNS_7MessageE]+0x73): undefined reference to `testing::Message::GetString() const'
What is missing in CMake configuration for tests? Why linker cannot find some gtest functions only when compiled with clang
?
My environment:
gcc
: 13.2.1clang
: 17.0.6cmake
: 3.28.1ld
: 2.41.0
Solution
As I had already pointed out in the comments, the issue is that target_compile_options(${PROJECT_NAME} PUBLIC -stdlib=libc++)
only sets the standard library to libc++ for the main executable, but not the gtest and gmock libraries, which is why they're built with libstdc++. Because libc++ and libstdc++ are not ABI compatible linking the executable to the test libraries fails.
The proper way to set toolchain-related flags in CMake is to use a toolchain file. For example, you can use the following clang-toolchain.cmake
file
# clang-toolchain.cmake
set(CMAKE_C_COMPILER clang)
set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(CMAKE_CXX_FLAGS_INIT "-stdlib=libc++")
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-lc++ -lc++abi")
set(CMAKE_EXE_LINKER_FLAGS_INIT "-lc++ -lc++abi")
set(CMAKE_MODULE_LINKER_FLAGS_INIT "-lc++ -lc++abi")
Then remove all toolchain-related content from your CMakeLists.txt
# CMakeLists.txt
cmake_minimum_required(VERSION 3.26)
project(cpp_stream_socket_tests)
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip
)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
enable_testing()
file(GLOB SRCS "src/*.cpp")
file(GLOB HEADERS "include/*.hpp")
add_executable(${PROJECT_NAME})
target_sources(${PROJECT_NAME} PRIVATE
${SRCS}
PRIVATE FILE_SET HEADERS BASE_DIRS include FILES ${HEADERS}
)
target_link_libraries(${PROJECT_NAME} PRIVATE gtest_main cpp_stream_socket)
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_23)
add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME})
You can then configure and build the project with clang by calling
cmake -B build/clang -S . --toolchain clang-toolchain.cmake
cmake --build build/clang
Answered By - Corristo Answer Checked By - Willingham (WPSolving Volunteer)