Search code examples
c++gitcmakemingwexternal-project

CMake Link error 1 using ExternalProject_Add


I recently switched the build system of my C++ project to CMake. I am trying to use the ExternalProject_Add function to download the required libraries(there are currently 3 of them, GLM and TINYOBJ are static and GLFW can be either static or dynamic) using git then link to them in my project. I want to be able to link these libraries (and possibly others) with minimal effort so that I can build on multiple platforms. Or if someone else comes in to work on the project, they won't have to worry too much about getting the correct libraries installed.

However, I keep getting these errors when building (on Windows 10 with MinGW):

[100%] Linking CXX executable app\OpenGLTest.exe
CMakeFiles\OpenGLTest.dir/objects.a(Main.cpp.obj):Main.cpp:(.text+0xd): undefined reference to `FPSCounter::getElapsedTime()'
CMakeFiles\OpenGLTest.dir/objects.a(Main.cpp.obj):Main.cpp:(.text+0x2b): undefined reference to `FPSCounter::reset()'
CMakeFiles\OpenGLTest.dir/objects.a(Main.cpp.obj):Main.cpp:(.text+0x54): undefined reference to `FPSCounter::setLastTick()'
CMakeFiles\OpenGLTest.dir/objects.a(Main.cpp.obj):Main.cpp:(.text+0x5e): undefined reference to `FPSCounter::addFrame()'
CMakeFiles\OpenGLTest.dir/objects.a(Main.cpp.obj):Main.cpp:(.text+0x12d): undefined reference to `GLCamera::getCameraZoom()'
CMakeFiles\OpenGLTest.dir/objects.a(Main.cpp.obj):Main.cpp:(.text+0x149): undefined reference to `GLCamera::setCameraZoom(float)'
CMakeFiles\OpenGLTest.dir/objects.a(Main.cpp.obj):Main.cpp:(.text+0x1de): undefined reference to `GLCamera::getCameraPosition()'
CMakeFiles\OpenGLTest.dir/objects.a(Main.cpp.obj):Main.cpp:(.text+0x747): undefined reference to `GLCamera::setCameraTarget(glm::tvec3<float, (glm::precision)0>)'
CMakeFiles\OpenGLTest.dir/objects.a(Main.cpp.obj):Main.cpp:(.text+0x771): undefined reference to `GLCamera::setCameraPosition(glm::tvec3<float, (glm::precision)0>)'
CMakeFiles\OpenGLTest.dir/objects.a(Main.cpp.obj):Main.cpp:(.text+0x968): undefined reference to `GLRenderer_Deferred::GLRenderer_Deferred()'
CMakeFiles\OpenGLTest.dir/objects.a(Main.cpp.obj):Main.cpp:(.text+0xb98): undefined reference to `FPSCounter::FPSCounter()'
CMakeFiles\OpenGLTest.dir/objects.a(Main.cpp.obj):Main.cpp:(.text+0xba2): undefined reference to `FPSCounter::FPSCounter()'
collect2.exe: error: ld returned 1 exit status
CMakeFiles\OpenGLTest.dir\build.make:98: recipe for target 'app/OpenGLTest.exe' failed
mingw32-make[2]: *** [app/OpenGLTest.exe] Error 1
CMakeFiles\Makefile2:66: recipe for target 'CMakeFiles/OpenGLTest.dir/all' failed
mingw32-make[1]: *** [CMakeFiles/OpenGLTest.dir/all] Error 2
Makefile:126: recipe for target 'all' failed
mingw32-make: *** [all] Error 2

My directory structure looks like this:

|-Project
  |-BUILD (all the CMake output files are here)
  | |-app (this is where the .exe is output to)
  | |-downloads (dependencies are downloaded here)
  |   |-deps
  |-OpenGL (this is the source directory)
    |-deps-CMakeLists.txt
    |-CMakeLists.txt
    |-src
      |-Main.cpp
      |-**Other source files and headers of the "undefined reference" errors are in this directory**
      |-RenderSystem
        |-More Source files

Here is my CMakeLists.txt:

cmake_minimum_required(VERSION 3.2)
project(OpenGLTest)
set(CMAKE_CXX_FLAGS "-std=c++11")

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/app)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(EX_PROJ_SOURCE_DIR ${CMAKE_BINARY_DIR}/downloads/deps/Source)
set(EX_PROJ_BUILD_DIR ${CMAKE_BINARY_DIR}/downloads/deps/Build)

# Include OpenGL
find_package(OpenGL REQUIRED)
if (OPENGL_FOUND)
    include_directories(${OPENGL_INCLUDE_DIR})
endif()

# Include GLEW
find_package(GLEW REQUIRED)
if (GLEW_FOUND)
    include_directories(${GLEW_INCLUDE_DIRS})
endif()

set(GLFW_LIB_DIR ${EX_PROJ_BUILD_DIR}/GLFW_EX/src)
link_directories(${GLFW_LIB_DIR})

# Download and unpack gtest at configure time
configure_file(deps-CMakeLists.txt downloads/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/downloads)
execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/downloads)

# Add gtest directly to our build
add_subdirectory(${EX_PROJ_SOURCE_DIR}/GLM_EX
                 ${EX_PROJ_BUILD_DIR}/GLM_EX
                 EXCLUDE_FROM_ALL )
add_subdirectory(${EX_PROJ_SOURCE_DIR}/GLFW_EX
                 ${EX_PROJ_BUILD_DIR}/GLFW_EX
                 EXCLUDE_FROM_ALL )
add_subdirectory(${EX_PROJ_SOURCE_DIR}/TINYOBJ_EX
                 ${EX_PROJ_BUILD_DIR}/TINYOBJ_EX
                 EXCLUDE_FROM_ALL )

# Add the gtest include directory, since gtest
# doesn't add that dependency to its gtest target
include_directories(${EX_PROJ_SOURCE_DIR}/GLM_EX/glm
                    ${EX_PROJ_SOURCE_DIR}/GLFW_EX/include
                    ${EX_PROJ_SOURCE_DIR}/TINYOBJ)

# add the executable
add_executable(OpenGLTest src/Main.cpp)
target_link_libraries(OpenGLTest tinyobjloader glm glfw3 ${GLEW_LIBRARIES} ${OPENGL_LIBRARIES})

add_custom_command(TARGET OpenGLTest POST_BUILD        # Adds a post-build event to MyTest
    COMMAND ${CMAKE_COMMAND} -E copy_if_different     # which executes "cmake - E copy_if_different..."
        "${GLFW_LIB_DIR}/glfw3.dll"                   # <--this is in-file
        $<TARGET_FILE_DIR:OpenGLTest>)                 # <--this is out-file path

This is the deps-CMakeLists.txt file:

cmake_minimum_required(VERSION 3.2)
project(deps-download LANGUAGES NONE)

include(ExternalProject)
set_directory_properties(PROPERTIES EP_BASE "./deps")

# Include GLFW
ExternalProject_Add (
       GLFW_EX
       GIT_REPOSITORY "https://github.com/glfw/glfw.git"
       GIT_TAG "master"
       CMAKE_ARGS -DGLFW_BUILD_EXAMPLES=OFF
                  -DGLFW_BUILD_TESTS=OFF
                  -DGLFW_BUILD_DOCS=OFF
                  -DGLFW_INSTALL=OFF
                  -DBUILD_SHARED_LIBS=ON
       UPDATE_COMMAND    ""
       INSTALL_COMMAND   ""
       TEST_COMMAND      "")

# Include GLM
ExternalProject_Add (
       GLM_EX
       GIT_REPOSITORY "https://github.com/g-truc/glm.git"
       GIT_TAG "master"
       UPDATE_COMMAND    ""
       BUILD_COMMAND     ""
       INSTALL_COMMAND   ""
       TEST_COMMAND      "")

# Include TINYOBJ
ExternalProject_Add (
       TINYOBJ_EX
       GIT_REPOSITORY "https://github.com/syoyo/tinyobjloader.git"
       GIT_TAG "master"
       UPDATE_COMMAND    ""
       BUILD_COMMAND     ""
       INSTALL_COMMAND   ""
       TEST_COMMAND      "")

add_dependencies(GLFW_EX GLM_EX TINYOBJ_EX)

My "main" is located in Main.cpp in the "src" directory along with all the files referenced in the errors as "undefined reference". I've added the include directories for all the libraries(right after the ExternalProject_Add command) and attempted to link the dynamic library being built for GLFW but it still doesn't seem to work.

What am I missing to get this to build correctly? Any help would be appreciated.

UPDATE:

I've shuffled some things around and moved the ExternalProject_Add commands to another file which are executed during the configure phase of my build, as suggested by Craig Scott. I've made sure that all the external libraries are linked. I even tested each library separately in a different test project to make sure the CMake files work.

All of the "undefined references" I'm getting are from files that I wrote, and are in my source tree. How/Why are they not being included?

Note: I have also tried to include the "src" directory but doesn't seem to do much of anything.


Solution

  • The main problem I was having was caused by not adding the source files to the final executable. I fixed the issue by adding a file(GLOB... just before the add_executable command like this:

    # get all *.cpp files recursively
    file(GLOB_RECURSE SRC_FILES ${PROJECT_SOURCE_DIR}/*.cpp)
    # add the executable
    add_executable(OpenGLTest ${SRC_FILES})
    

    I will probably move to a solution that involves a more explicit way of adding source files in the future since GLOBs are not recommended.

    Thanks to Craig Scott for your help.