Недавно я переключил систему сборки моего C ++ проекта на CMake. Я пытаюсь использовать функцию ExternalProject_Add для загрузки необходимых библиотек (в настоящее время их 3, GLM и TINYOBJ являются статическими, а GLFW могут быть статическими или динамическими), используя git, а затем ссылки на них в моем проекте. Я хочу иметь возможность связать эти библиотеки (и, возможно, другие) с минимальными усилиями, чтобы я мог строить на нескольких платформах. Или, если кто-то еще придет, чтобы поработать над проектом, им не придется слишком сильно беспокоиться об установке правильных библиотек.
Тем не менее, я продолжаю получать эти ошибки при сборке (на Windows 10 с 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
Моя структура каталогов выглядит так:
|-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
Вот мой 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
Это файл deps-CMakeLists.txt:
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)
Мой «main» находится в Main.cpp в каталоге «src» вместе со всеми файлами, на которые ссылаются в ошибках как «неопределенная ссылка». Я добавил каталоги include для всех библиотек (сразу после команды ExternalProject_Add) и попытался связать динамическую библиотеку, создаваемую для GLFW, но она все еще не работает.
Чего мне не хватает, чтобы это правильно построить? Любая помощь будет оценена.
ОБНОВИТЬ:
Я перемешал некоторые вещи и переместил ExternalProject_Add
команды для другого файла, которые выполняются на этапе настройки моей сборки, как это предложил Крейг Скотт. Я убедился, что все внешние библиотеки связаны между собой. Я даже протестировал каждую библиотеку отдельно в отдельном тестовом проекте, чтобы убедиться, что файлы CMake работают.
Все «неопределенные ссылки», которые я получаю, взяты из файлов, которые я написал, и находятся в моем исходном дереве. Как / почему они не включены?
Примечание: я также попытался включить каталог «src», но, похоже, ничего не делает.
Пока ваш файл CMakeLists.txt создает внешние проекты, ваша цель OpenGLTest не ссылается на них. Предположительно, именно они предоставляют недостающие символы, на которые жалуется ваш компоновщик. Простое добавление этих внешних проектов в качестве зависимостей также не добавит их в вашу цель OpenGLTest.
Чтобы решить эту проблему, вам нужно добавить внешние библиотеки target_link_libraries
команда. К сожалению, эти библиотеки не будут существовать, когда вы запустите CMake (ну, во всяком случае, не в первый раз), поэтому вам придется вручную разобраться в деталях библиотек (но см. Альтернативу ниже). Может быть достаточно знать только каталог, в котором ExternalProject
поставлю их. Это должно быть предсказуемо для каждой сборки, чтобы вы могли понять, что это такое, посмотрев одну из ваших тестовых сборок. Я полагаю, что вы можете просто добавить этот путь к пути поиска компоновщика и перечислить базовое имя библиотеки в target_link_libraries
команда (базовое имя означает отбрасывание любой ведущей «lib» на Unix-подобных платформах, а также суффикс файла). Если это не сработает, вам нужно будет создать полный путь к библиотеке и добавить его в target_link_libraries
команда вместо Это потребует дополнительной работы, особенно если вы хотите построить на нескольких платформах (здесь может пригодиться набор переменных CMake CMAKE _…_ LIBRARY_PREFIX и CMAKE _…_ LIBRARY_SUFFIX).
Если ручное указание деталей библиотеки вас беспокоит, и если внешние проекты также используют CMake, есть способ получить ExternalProject
скачать исходники для вас, но затем использовать add_subdirectory
привнести их прямо в ваш проект. Затем они будут иметь цели CMake, которые вы можете использовать для указания на target_link_libraries
команда и будет иметь дополнительное преимущество, заключающееся в том, что он всегда создается с согласованными флагами компилятора / компоновщика, как и остальная часть вашего проекта. Техника обсуждается с Google Test в качестве примера здесь:
https://crascit.com/2015/07/25/cmake-gtest/
Если вы хотите, вы можете изменить этот подход, чтобы сделать всю сборку во время CMake, а затем использовать find_library
или аналогичный, но это сделало бы шаг CMake потенциально очень дорогостоящим и обычно не рекомендуется.
Основная проблема, с которой я столкнулся, заключалась в том, что я не добавил исходные файлы в конечный исполняемый файл. Я исправил проблему, добавив file(GLOB...
как раз перед add_executable
команда как это:
# get all *.cpp files recursively
file(GLOB_RECURSE SRC_FILES ${PROJECT_SOURCE_DIR}/*.cpp)
# add the executable
add_executable(OpenGLTest ${SRC_FILES})
Я, вероятно, перейду к решению, которое включает более явный способ добавления исходных файлов в будущем, так как GLOB
не рекомендуется.
Спасибо Крейгу Скотту за вашу помощь.