CMake Link error 1 utilizando ExternalProject_Add

Recientemente cambié el sistema de compilation de mi proyecto C ++ a CMake. Estoy tratando de usar la function ExternalProject_Add para download las bibliotecas necesarias (actualmente hay 3 de ellas, GLM y TINYOBJ son estáticas y GLFW puede ser estática o dinámica) usando git y luego vincúlese a ellas en mi proyecto. Quiero poder vincular estas bibliotecas (y posiblemente otras) con el mínimo esfuerzo para poder build en múltiples plataforms. O si alguien más entra a trabajar en el proyecto, no tendrá que preocuparse demasiado por instalar las bibliotecas correctas.

Sin embargo, sigo recibiendo estos errores al build (en Windows 10 con 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_Defernetworking::GLRenderer_Defernetworking()' 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 

Mi estructura de directory se ve así:

 |-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 

Aquí está mi CMakeLists.txt:

 cmake_minimum_requinetworking(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 

Este es el file deps-CMakeLists.txt:

 cmake_minimum_requinetworking(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) 

Mi "principal" se encuentra en Main.cpp en el directory "src" junto con todos los files a los que se hace reference en los errores como "reference no definida". He agregado los directorys de inclusión para todas las bibliotecas (justo después del command ExternalProject_Add) e intenté vincular la biblioteca dinámica que se está construyendo para GLFW, pero parece que todavía no funciona.

¿Qué me estoy perdiendo para que esto se construya correctamente? Cualquier ayuda sería apreciada.

ACTUALIZAR:

He barajado algunas cosas y moví los commands ExternalProject_Add a otro file que se ejecutan durante la fase de configuration de mi compilation, como sugiere Craig Scott. Me he asegurado de que todas las bibliotecas externas estén vinculadas. Incluso probé cada biblioteca por separado en un proyecto de testing diferente para asegurarme de que los files CMake funcionen.

Todas las "references indefinidas" que recibo provienen de files que escribí, y están en mi tree fuente. ¿Cómo / Por qué no están siendo incluidos?

Nota: También he intentado include el directory "src" pero parece que no hace mucho.

Mientras que su file CMakeLists.txt construye los proyectos externos, su objective OpenGLTest no se vincula a ellos. Presumiblemente, son los que proporcionan los símbolos faltantes de los que su linker se queja. Simplemente agregar esos proyectos externos como dependencies tampoco los agregará a su objective OpenGLTest.

Para solucionar este problema, debe agregar las bibliotecas externas a su command target_link_libraries . Lamentablemente, esas bibliotecas no existirán cuando ejecute CMake (bueno, no es la primera vez, de todos modos), por lo que debe resolver manualmente los detalles de las bibliotecas (pero consulte más adelante para get una alternativa). Puede ser suficiente saber solo el directory en el que ExternalProject colocará. Debería ser pnetworkingecible para cada compilation, por lo que puedes averiguar qué es mirando una de tus comstackciones de testing. Creo que entonces puede agregar esa ruta a la ruta de búsqueda del vinculador y enumerar el nombre base de la biblioteca en el command target_link_libraries (el nombre base significa colocar cualquier "lib" inicial en plataforms unix así como el sufijo del file). Si eso no funciona, deberá build la ruta completa a la biblioteca y agregarla al command target_link_libraries lugar. Esto requeriría más trabajo, especialmente si desea build en múltiples plataforms (el set de variables CMake CMAKE _…_ LIBRARY_PREFIX y CMAKE _…_ LIBRARY_SUFFIX pueden ser útiles aquí).

Si especificar manualmente los detalles de la biblioteca te molesta y si los proyectos externos también usan CMake, hay una forma de que ExternalProject descargue las fonts por ti, pero luego usa add_subdirectory para llevarlas directamente a tu proyecto. Luego, tendrían los objectives de CMake que podría usar para especificar en su command target_link_libraries y tendrían el beneficio adicional de estar siempre construidos con indicadores de comstackdor / linker consistentes como el rest de su proyecto. La técnica se analiza con Google Test como ejemplo aquí:

https://crascit.com/2015/07/25/cmake-gtest/

Si quisieras, podrías modificar ese enfoque para hacer toda la compilation en CMake time y luego usar find_library o similar, pero eso haría que el paso de CMake sea potencialmente muy costoso y normalmente no se recomienda.

El principal problema que estaba teniendo fue causado por no agregar los files fuente al ejecutable final. file(GLOB... el problema agregando un file(GLOB... justo antes del command add_executable como este:

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

Probablemente pase a una solución que implique una forma más explícita de agregar files fuente en el futuro, ya que no se recomiendan los GLOB .

Gracias a Craig Scott por tu ayuda.