I have a problem with running gtest using cmake. This started after I changed the project structure, dividing the code into several modules with separate CMakeList.txt files. When I had one CmakeList without a static library, everything works fine.
Now I have this project structure:
├── src
│ ├── core
│ │ ├── bot.cpp
│ │ ├── bot.h
│ │ ├── domain.h
│ │ ├── geometry.h
│ │ ├── memory_reader.cpp
│ │ ├── memory_reader.h
│ │ ├── process_tools.cpp
│ │ ├── process_tools.h
│ │ └── CMakeLists.txt
│ └── main.cpp
├── test
│ ├── bot_test.cpp
│ ├── geometry_test.cpp
│ └── CMakeLists.txt
└── CMakeLists.txt
CMakeLists.txt:
cmake_minimum_required(VERSION 3.26)
project(ZumaBot)
set(CMAKE_CXX_STANDARD 17)
add_compile_options(-Werror -Wall -Wextra)
set(OpenCV_DIR "C:/DevTools/opencv/install")
find_package(OpenCV REQUIRED)
add_subdirectory("src/core")
add_subdirectory("tests")
if (OpenCV_FOUND)
add_executable(main-bot WIN32 src/main.cpp)
target_link_libraries(main-bot
core
${OpenCV_LIBS}
gdiplus
ntdll)
endif ()
src/core/CMakeLists.txt:
cmake_minimum_required(VERSION 3.26)
project(ZumaBot)
set(CMAKE_CXX_STANDARD 17)
set(BOOST_ROOT "C:/DevTools/boost_1_85_0")
set(Boost_ARCHITECTURE "-x32")
find_package(Boost REQUIRED)
if (Boost_FOUND)
add_library(core STATIC
bot.h
bot.cpp
domain.h
geometry.h
memory_reader.h
memory_reader.cpp
process_tools.h
process_tools.cpp)
target_link_libraries(core ${Boost_LIBRARIES})
include_directories(SYSTEM ${Boost_INCLUDE_DIR})
target_include_directories(core
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}
${Boost_INCLUDE_DIR}
)
endif ()
tests/CMakeLists.txt:
cmake_minimum_required(VERSION 3.26)
project(ZumaBot)
set(CMAKE_CXX_STANDARD 17)
include(FetchContent)
FetchContent_Declare(
googletest
# Specify the commit you depend on and update it regularl y.
URL https://github.com/google/googletest/archive/5376968f6948923e2411081fd9372e71a59d8e77.zip
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
add_executable(test
geometry_test.cpp
bot_test.cpp)
target_link_libraries(test
core
gtest_main
)
With this structure I can run main-bot
target without any problem, but if I run the target with tests I get the following errors:
====================[ Build | test | Debug ]====================================
"C:Program FilesJetBrainsCLion 2023.2.2bincmakewinx64bincmake.exe" --build C:UsersArtemCLionProjectsZumaBotcmake-build-debug --target test -j 6
[1/1] Linking CXX executable teststest.exe
FAILED: tests/test.exe
cmd.exe /C "cd . && C:PROGRA~1JETBRA~1CLION2~1.2binmingwbinG__~1.EXE -g tests/CMakeFiles/test.dir/geometry_test.cpp.obj tests/CMakeFiles/test.dir/bot_test.cpp.obj -o teststest.exe -Wl,--out-implib,testslibtest.dll.a -Wl,--major-image-version,0,--minor-image-version,0 src/core/libcore.a lib/libgtest_main.a lib/libgtest.a -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 && cd ."
C:Program FilesJetBrainsCLion 2023.2.2binmingwbin/ld.exe: src/core/libcore.a(bot.cpp.obj): in function `void boost::geometry::strategy::intersection::cartesian_segments<void>::segment_intersection_info<float, boost::geometry::segment_ratio<float> >::assign_a<boost::geometry::model::point<float, 2ull, boost::geometry::cs::cartesian>, boost::geometry::model::referring_segment<boost::geometry::model::point<float, 2ull, boost::geometry::cs::cartesian> const>, boost::geometry::model::referring_segment<boost::geometry::model::point<float, 2ull, boost::geometry::cs::cartesian> const> >(boost::geometry::model::point<float, 2ull, boost::geometry::cs::cartesian>&, boost::geometry::model::referring_segment<boost::geometry::model::point<float, 2ull, boost::geometry::cs::cartesian> const> const&, boost::geometry::model::referring_segment<boost::geometry::model::point<float, 2ull, boost::geometry::cs::cartesian> const> const&) const':
C:/Users/Artem/CLionProjects/ZumaBot/src/core/geometry.h:18: multiple definition of `zuma_bot::geometry::detail::createLine(std::vector<zuma_bot::Coord, std::allocator<zuma_bot::Coord> >)'; tests/CMakeFiles/test.dir/geometry_test.cpp.obj:C:/Users/Artem/CLionProjects/ZumaBot/src/core/geometry.h:18: first defined here
C:Program FilesJetBrainsCLion 2023.2.2binmingwbin/ld.exe: src/core/libcore.a(bot.cpp.obj): in function `std::pair<bool, bool> boost::geometry::strategy::intersection::cartesian_segments<void>::is_x_more_significant<float>(float const&, float const&, float const&, float const&, bool, bool)':
C:/Users/Artem/CLionProjects/ZumaBot/src/core/geometry.h:27: multiple definition of `zuma_bot::geometry::Intersection(std::vector<zuma_bot::Coord, std::allocator<zuma_bot::Coord> > const&, std::vector<zuma_bot::Coord, std::allocator<zuma_bot::Coord> > const&)'; tests/CMakeFiles/test.dir/geometry_test.cpp.obj:C:/Users/Artem/CLionProjects/ZumaBot/src/core/geometry.h:27: first defined here
collect2.exe: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
If I understand correctly, the problem is that geometry.h
is compiled into the core library and also used as an include, but I can’t understand how this can be fixed.
The geometry.h
file itself looks like this, but I don’t think that problem in it:
#pragma once
#include "domain.h"
#include <algorithm>
#include <vector>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>
namespace zuma_bot::geometry {
namespace detail {
namespace bg = boost::geometry;
typedef bg::model::point<float, 2, bg::cs::cartesian> point_t;
typedef bg::model::linestring <point_t> linestring_t;
typedef bg::model::multi_point <point_t> mpoint_t;
linestring_t createLine(std::vector<Coord> line) {
linestring_t ls;
std::for_each(line.begin(), line.end(), [&ls](Coord c) {
bg::append(ls, point_t(c.x, c.y));
});
return ls;
}
}
std::vector<Coord> Intersection(const std::vector<Coord> &line_1, const std::vector<Coord> &line_2) {
using namespace detail;
linestring_t ls_1 = createLine(line_1);
linestring_t ls_2 = createLine(line_2);
mpoint_t out;
bg::intersection(ls_1, ls_2, out);
std::vector<Coord> result;
result.reserve(out.size());
std::for_each(out.begin(), out.end(), [&result](point_t p) {
result.push_back({p.get<0>(), p.get<1>()});
});
return result;
}
} // zuma_bot::geometry;
And how it is used in tests (geometry_test.cpp
):
#include <vector>
#include <gtest/gtest.h>
#include "geometry.h"
using namespace zuma_bot;
using namespace zuma_bot::geometry;
using namespace std;
TEST(Intersection, Intersection_1) {
vector<Coord> line_1{{0, 0}, {0, 5}};
vector<Coord> line_2{{1, 1}, {-1, 1}};
auto result = Intersection(line_1, line_2);
ASSERT_EQ(result.size(), 1);
EXPECT_EQ(result[0], (Coord{0, 1}));
}
TEST(Intersection, Intersection_2) {
vector<Coord> line_1{{0, 0}, {0, 5}};
vector<Coord> line_2{{0, 0}, {-1, 1}};
auto result = Intersection(line_1, line_2);
ASSERT_EQ(result.size(), 1);
EXPECT_EQ(result[0], (Coord{0, 0}));
}
...
If you need full project here
I tried to separate the header files of the library into a another target, and also use target_include_directories
differently, but I just got more confused(
Владимиров Артём is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.