Некоторое время назад мы познакомились с Autotools . Несмотря на то, что Autotools до сих пор используется во многих известных проектах с открытым исходным кодом, инструмент этот трудно назвать особо удобным. Кроме того, нормально работает он только в *nix системах, а в каком-нибудь Windows пользоваться Autotools, скажем так, весьма непросто. В общем, Autotools — это легаси, и нормальные программисты в наше время пытаются использовать CMake или, например, SCons. В этой заметке мы познакомимся с CMake.
Говоря простыми словами, CMake — это такая штука, в которой вы описываете проект, а она вам генерирует Makefile’ы в *nix системах, проекты Visual Studio под Windows, файлы конкретных редакторов и IDE, например Sublime Text , Code::Blocks , Eclipse или KDevelop, и так далее. Несмотря на спорный в некоторых моментах синтаксис, в последнее время CMake становится стандартом де-факто в мире C/C++. В частности, CMake используется в LLVM , Qt, MariaDB, Blender, KiCad , GNU Radio и ряде других проектов . Кроме того, в CLion, IDE для C/C++ от компании JetBrains, по умолчанию также создаются проекты, основанные на CMake.
Примечание: В этом контексте вас может заинтересовать заметка Как править в CLion код любых проектов на С++, даже тех, в которых не используется CMake .
Использование CMake в простейшем случае выглядит следующим образом. В корне репозитория создается файл CMakeLists.txt примерно такого содержания:
# так пишутся комментарии
project ( project_name )
find_library ( PTHREAD_LIBRARY pthread )
find_library ( PCRE_LIBRARY pcre )
include_directories ( include )
set ( CMAKE_CXX_STANDARD 17 )
set ( CMAKE_CXX_STANDARD_REQUIRED on )
set ( CMAKE_CXX_FLAGS » ${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror» )
add_executable ( main src/Main.cpp src/HttpServer.cpp )
target_link_libraries ( main ${PTHREAD_LIBRARY} ${PCRE_LIBRARY} )
Хочется надеяться, какая строчка здесь что означает, пояснять не нужно. Затем исходники складываются в каталог src, а заголовочные файлы — в каталог include. Для сборки проекта говорим:
cd build
cmake ..
make
Просто, не правда ли?
Помимо приведенного выше find_library
в CMake есть ряд скриптов для подключения конкретных библиотек. В частности, подключение OpenGL осуществляется как-то так:
include_directories ( ${OPENGL_INCLUDE_DIR} )
target_link_libraries ( main ${OPENGL_LIBRARY} ${CMAKE_DL_LIBS} )
CMake можно указать конкретный тип Makefile’ов, которые вы хотите получить на выходе:
cmake -G «MinGW Makefiles» ..
# для просмотра списка всех доступных генераторов:
cmake -G
В частности, многие программисты для ускорения сборки проектов предпочитают использовать Ninja :
ninja -j1
Выбор между отладочной и релизной сборкой осуществляется так:
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -G Ninja ..
cmake -DCMAKE_BUILD_TYPE=MinSizeRel -G Ninja ..
# Debug используется по умолчанию
cmake -DCMAKE_BUILD_TYPE=Debug -G Ninja ..
Вместо запуска напрямую make или ninja можно сказать что-то вроде:
Можно выбрать конкретный компилятор для сборки проекта
-DCMAKE_CXX_COMPILER= ` which clang++ ` -G Ninja ..
… а также указать дополнительные флаги компиляции:
Например, для определения степени покрытия кода тестами при помощи lcov нужно сказать что-то вроде:
-DCMAKE_EXE_LINKER_FLAGS= «-lgcov» ..
В мире C/C++ нередко бывает, что сторонние библиотеки, использующие CMake, подключаются к проекту при помощи сабмодулей Git . Подключение таких библиотек к проекту осуществляется довольно просто:
project ( c-algorithms-examples )
include_directories ( deps/algorithms/include )
add_subdirectory ( deps/algorithms/src )
add_executable ( rbtree_example rbtree_example.c )
target_link_libraries ( rbtree_example CAlgorithms )
В свою очередь, у библиотеки файл src/CMakeList.txt должен быть примерно таким:
project ( c-algorithms )
add_library ( CAlgorithms STATIC
struct/ilist.c
struct/rbtree.c
struct/htable.c
common/utils.c
)
Вообще, add_subdirectory может принимать путь до любого каталога, в котором есть файл CMakeLists.txt. Это позволяет разбивать проект на подпроекты даже в рамках одного репозитория. Опять же, в случае с библиотеками это позволяет поместить тесты в отдельный подпроект, который не будет собираться при подключении библиотеки в сторонние проекты.
Например, в корне библиотеки CMakeList.txt может быть таким:
project ( c-algorithms-root )
enable_testing ()
include_directories ( include )
add_subdirectory ( src )
add_subdirectory ( test )
Непосредственно тесты добавляются в проект следующим образом:
project ( c-algorithms-struct-tests )
set ( CMAKE_C_FLAGS » ${CMAKE_C_FLAGS} -O0 -g» )
add_executable ( test_htable test_htable.c )
target_link_libraries ( test_htable CAlgorithms )
add_executable ( test_rbtree test_rbtree.c )
target_link_libraries ( test_rbtree CAlgorithms )
add_test ( test_htable «./test_htable» )
add_test ( test_rbtree «./test_rbtree» )
Запуск тестов осуществляется простой командой:
# или, с включением отладочного вывода:
make test ARGS = «-V»
# или, если используете Ninja:
ninja test
… выполненной в каталоге build. Если вас интересует тема написания модульных тестов на C++, она более подробно раскрыта в заметке Тестирование кода на C++ с помощью Google Test .
Если же вы используете какой-нибудь PyTest , просто допишите в CMakeList.txt что-то вроде:
enable_testing ()
add_test ( NAME python_test
COMMAND py.test —capture=no ${CMAKE_SOURCE_DIR} /tests/run.py )
Вывод тестов пишется в файл Testing/Temporary/LastTest.log. Кстати, подробности о переменных окружения, доступных в CMake, таких, как CMAKE_SOURCE_DIR, можно найти здесь .
Помимо рассмотренных выше возможностей часто можно встретить поддержку сборки проектов с различными опциями. В частности, это используется в Assimp и LLDB . При сборке проекта опции выбираются так:
cmake -DASSIMP_BUILD_ASSIMP_TOOLS=OFF …
Опции обычно описывают в документации, но в крайнем случае их можно посмотреть и через curses-интерфейс:
В рамках одного поста, конечно, не представляется возможным рассмотреть все возможности CMake. Однако представленной выше информации вам должно вполне хватить в 90% случаев. Полноценные рабочие примеры использования CMake вы найдете, например, в этом , этом , а также в этом репозиториях на GitHub. Примеры использования опций и условных операторов можно найти в репозиториях уже упомянутых Assimp и LLDB . Ну и, конечно же, массу полезного вы найдете на официальном сайте CMake .
А пользуетесь ли вы CMake и если да, используете ли какие-то его возможности, о которых не было рассказано выше?