Я новичок в C++, и недавно я выяснял процесс загрузки внешней библиотеки и связывания ее с проектом, над которым я работаю. Судя по языкам, где добавление новых зависимостей и установка необходимых являются одной командой, кажется, что есть несколько способов загрузить и связать библиотеку на C++, и поэтому я изо всех сил пытаюсь представить, как опытные разработчики с крупномасштабными проектами могут сделать их скрипты и библиотеки переносимыми и простыми в установке.
Чтобы привести пример, в моем проекте я использовал файл CMakeLists.txt
с жестко заданным путем к библиотеке, которую я использую, libxml2, что-то вроде этого:
include_directories(/opt/homebrew/Cellar/libxml2/2.10.3_1/include);
link_directories(/opt/homebrew/Cellar/libxml2/2.10.3_1/lib);
Но это потребует отдельной загрузки libxml2 на каждую машину, на которой клонируется этот проект, и путь также будет меняться в зависимости от версии и среды. Итак, мой конкретный вопрос: что считается лучшей практикой для упаковки проектов C++ с внешними зависимостями?
Например, в node.js есть package.json
и npm install
, в python — requirement.txt
и pip install -r requirements.txt
. Есть ли эквивалент C++?
Извините, если это глупый вопрос, но я действительно изо всех сил пытаюсь найти правильные условия поиска, чтобы найти правильный ответ. Заранее спасибо!
Лично я просто использую git-модули с зависимостями как часть моего репозитория, поэтому, когда кто-то проверяет его, он/она также может получить весь исходный код, необходимый для сборки/связывания необходимых зависимостей.
К счастью, многие популярные библиотеки сами созданы с помощью CMake, поэтому вам не нужно делать ничего особенного, чтобы добавить их в свой проект CMake. Модули также относятся к конкретной фиксации репозитория зависимостей, и это помогает поддерживать согласованную версию зависимостей между участниками.
Например. вот как выглядит файловая структура в одном из моих проектов (я избавился от некоторых деталей, которые напрямую не связаны с вопросом):
|-- CMakeLists.txt
|-- CMakeSettings.json
|-- README.md
|-- assets
| |-- assets-files
|-- lib
| |-- CMakeLists.txt
| |-- libTwo
| |-- libThree
| `-- libFour
`-- src
|-- source-files
Каждая подпапка в lib
является отдельным подмодулем в моем репозитории. В корень CMakeLists.txt
я просто добавляю эту папку со своими зависимостями одной строкой конфигурации:
add_subdirectory(lib)
В то время как CMakeLists.txt
в папке lib позаботится обо всем остальном:
add_compile_options(-w)
# Lib One
find_package(libOne REQUIRED)
list(APPEND LIB_TARGETS ${LIB_ONE_LIBRARIES})
list(APPEND LIB_INCLUDE_DIRS ${LIB_ONE_INCLUDE_DIRS})
# Lib Two
add_subdirectory(libTwo)
list(APPEND LIB_TARGETS libTwo)
list(APPEND LIB_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/libTwo/include")
# Lib Three
add_subdirectory(libThree)
list(APPEND LIB_TARGETS libThreeProj)
list(APPEND LIB_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/libThree/include")
# Lib Four
add_subdirectory(libFour)
list(APPEND LIB_TARGETS libFour)
if (LIB_INCLUDE_DIRS)
list(REMOVE_DUPLICATES LIB_INCLUDE_DIRS)
target_include_directories(${PROJ_NAME} PRIVATE ${LIB_INCLUDE_DIRS})
if (MSVC)
target_compile_options(${PROJ_NAME} PRIVATE
"$<$<CXX_COMPILER_ID:Clang>:SHELL:-Xclang -isystem$<JOIN:${LIB_INCLUDE_DIRS}, -Xclang -isystem>>"
"$<$<CXX_COMPILER_ID:MSVC>:SHELL:-experimental:external -external:W0 -external:anglebrackets -external:I$<JOIN:${LIB_INCLUDE_DIRS}, -external:I>>")
else()
target_compile_options(${PROJ_NAME} PRIVATE "SHELL:-isystem $<JOIN:${LIB_INCLUDE_DIRS}, -isystem>")
endif ()
endif ()
target_link_libraries(${PROJ_NAME} PRIVATE ${LIB_TARGETS})
Вы можете заметить, что я также сохраняю другие зависимости (например, libOne
в данном случае), которые можно найти с помощью find_package
, в том же месте, и они точно совместимы со всеми зависимостями, которые я загружал из удаленных репозиториев.
Конечно, это не всегда работает гладко и сильно зависит от того, как авторы библиотек распространяют свой код, но для меня работает большую часть времени, даже когда это требует некоторых настроек в конфигурации (например, libFour
— это библиотека только для заголовков, и вообще не требует никакой привязки, а libThree
имеет несовместимое целевое имя libThreeProj
)
Ах, хорошо, теперь это имеет больше смысла. Я никогда не думал о клонировании репозиториев непосредственно в проект, но он выглядит чистым и хорошо работает, поэтому я понимаю, почему это жизнеспособное решение. Спасибо за подробный ответ!
К сожалению, ничего, что соответствовало бы вашим ожиданиям по сравнению с такими вещами, как npm и maven. См. здесь, а также vcpkg, о котором я не вижу упоминания в связанном посте.