6

I am rather new to C++ and CMake. I'm trying to make a library, and I'm getting the errors when I try to run my unit tests. From my research I've already gathered what the "Unresolved external symbol" errors mean, but I'm unable to figure out how to fix it.

Here's my project structure:

lib
  glfw - GLFW source folder
src
  ogl-renderer.cpp
  ogl-renderer.h
  CMakeLists.txt
  ...additional source files
test
  ogl-test.cpp
  ogl-test.h
  CMakeLists.txt
CMakeLists.txt

CMakeLists.txt:

cmake_minimum_required (VERSION 3.8)

project("ogl-renderer")

set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE)
set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)

add_subdirectory ("lib/glfw-3.3.2")

add_subdirectory ("src")
add_subdirectory ("test")

enable_testing()

add_test (ogl-renderer olg-test)

src/CMakeLists.txt:

cmake_minimum_required (VERSION 3.8)

add_library (ogl-renderer "engine/renderer.cpp" "engine/renderer.h" "engine/renderer.cpp" "engine/renderer.h" "engine/Window.cpp" "engine/Window.h" "engine/Shape.cpp" "engine/Shape.h" "engine/message-queue.cpp" "engine/message-queue.h" "engine/messages/window-mgmt.h" "engine/messages/window-mgmt.cpp")

target_link_libraries(ogl-renderer glfw)

test/CMakeLists.txt:

add_executable (ogl-test "ogl-test.cpp" "ogl-test.h")

target_link_libraries(ogl-test ogl-renderer)

test/ogl-test.cpp:

#include "../src/ogl-renderer.h"

void testWindow() {
    NglRenderer::startRenderer();

    int windowId = NglRenderer::createWindow("Test", 640, 480);
}

int main() {
    testWindow();
}

Errors:

Error   LNK2019 unresolved external symbol "void __cdecl NglRenderer::startRenderer(void)" (?startRenderer@NglRenderer@@YAXXZ) referenced in function "void __cdecl testWindow(void)" (?testWindow@@YAXXZ)  C:\Users\chansen\source\repos\ogl-renderer\out\build\x64-Debug\ogl-renderer C:\Users\chansen\source\repos\ogl-renderer\out\build\x64-Debug\ogl-test.cpp.obj 1

Error   LNK2019 unresolved external symbol "int __cdecl NglRenderer::createWindow(char *,int,int)" (?createWindow@NglRenderer@@YAHPEADHH@Z) referenced in function "void __cdecl testWindow(void)" (?testWindow@@YAXXZ) C:\Users\chansen\source\repos\ogl-renderer\out\build\x64-Debug\ogl-renderer C:\Users\chansen\source\repos\ogl-renderer\out\build\x64-Debug\ogl-test.cpp.obj 1

I am able to fix it by always including the .h and .cpp files in every location, but I want to figure this out the right way. I understand that I need to compile the source project into a library, then link that to the test executable, but I can't figure out how to do it correctly. I'm not even entirely certain how correct my CMakeList.txt files even are. I either guessed or copied on all these configurations.

chrispytoes
  • 1,714
  • 1
  • 20
  • 53
  • What library are `NglRenderer::startRenderer` and `NglRenderer::createWindow` in? – Stephen Newell Jul 10 '20 at 18:25
  • @StephenNewell `NglRenderer` is the namespace for my library, defined in the `src/ogl-renderer` – chrispytoes Jul 10 '20 at 18:26
  • I mean the actual library. You link two into your test: `glfw` and `ogl-renderer`. – Stephen Newell Jul 10 '20 at 18:27
  • @StephenNewell Ah right, GLFW is OpenGL. It's being used in the `src` folder. Now that I think of it I probably don't need to link it in the test. – chrispytoes Jul 10 '20 at 18:29
  • `add_library (ogl-renderer "` There is no need to do that on one long line, you add newlines between source files. `#include "../src/ogl-renderer.h"` just `target_include_directories(... ${CMAKE_CURRENT_SOURCE_DIR})` and do `#include `. `NglRenderer is the namespace for my library, defined in the src/ogl-renderer` So `ogl-renderer.cpp` is missing from `add_library`, so.... the symbol is missing. Why is it missing? Why don't you add it? `add_test (ogl-renderer olg-test)` looks strange,you want to run `ogl-renderer` exe? guess you want `add_test(NAME ogl-test COMMAND ogl-test)`. – KamilCuk Jul 16 '20 at 08:07
  • @KamilCuk They're only listed on one line because Visual Studio generates it that way. Also, `ogl-renderer` is not an exe, it is a library. I want to test my `ogl-renderer` library with the exe `ogl-test`. – chrispytoes Jul 16 '20 at 23:37
  • I see some strangeness in the way that you're setting up your projects. Typically, you'd `add_subdirectory()` to a folder where a `project` is listed. You wouldn't add the srcs and do add_library in the subdirectory. The error is telling you that the test project doesn't have the symbols for the `olg-renderer`. This might just be a scope issue as you define the project and add_library it in a subdirectory but then do the tests in the main level heirarchy again. – g19fanatic Jul 20 '20 at 12:41
  • Have you tried to look at your symbols to see what is actually available in them? `depends` can help with this. – g19fanatic Jul 20 '20 at 12:41

1 Answers1

1

Each CMakeLists.txt file defines a project to be compiled. And each project must contain all of its files, dependencies and build options.

So "test/CMakeLists.txt" must contain all relevant test source files. And "src/CMakeLists.txt" must contain all relevant src source files. This is why adding the .h and .cpp files fixed the error.

After each project is configured, linked libraries are defined with target_link_libraries().

For example in src the line target_link_libraries(ogl-renderer glfw) will link to glfw. And in test the line target_link_libraries(ogl-test ogl-renderer) will link to your src executable.

As long as the target_link_libraries() commands make sense, linking will be automated.

Some threads with more information:

Cosmin
  • 21,216
  • 5
  • 45
  • 60
  • So from what you're saying, mine looks like it follows all of that. What does a typical project configuration with tests actually look like? – chrispytoes Jul 16 '20 at 00:59
  • @chrispytoes, Yes, should be ok. enable_testing() is the standard way, added some links with more information. – Cosmin Jul 16 '20 at 07:31