Skip to content

Files

157 lines (119 loc) · 10.8 KB

File metadata and controls

157 lines (119 loc) · 10.8 KB

Система сборки cmake

Использование сторонних библиотек усложняет процесс воспроизводимости сборки. В случае, когда целевая операционная система одна, это не доставляет особых проблем и достаточно простого Makefile, но если предполагается разработка кросс-платформенного продукта, то возникают неоднозначности:

  • какой компилятор используется для сборки (gcc, clang, cl.exe);
  • расположение include-файлов библиотек для компиляции;
  • рссположение файлов библиотек для компоновки.

По этой причине часто практикуется не распространение Makefile, написанного для конкретного стека инструментов, а его генерация по декларативному описанию в процессе сборки: ./configure, qmake, или cmake.

Наиболее гибкой системой сборки, и при этом относительно простой, является CMake, которая реализована с поддержкой не только UNIX-подобных систем, но и Windows.

Проект CMake и его сборка

Описание проекта находится в файле CMakeLists.txt и имеет примерно следующий вид:

# признак того, что это файл для cmake
# номер версии - это минимально требуемый для сборки проекта
# не стоит злоупотребять указанием самой свежей версии
# CMake, поскольку в консервативных Linux-дистрибутивах
# может быть что-то более старое
cmake_minimum_required(VERSION 3.2)

# имя проекта - не обязательно, обычно используется IDE
project(my_great_project)

# команда `set` устанавливает значение переменной
# некоторые переменные (их имена начинаются с CMAKE_)
# имеют специальное значение

# дополнительные опции компилятора Си
set(CMAKE_C_FLAGS "-std=gnu11")

# дополнительные опции компилятора C++
set(CMAKE_CXX_FLAGS "-std=gnu14")

# в переменной SOURCES будет храниться список файлов;
# если файлов не много, то можно этого не делать,
# но некоторым IDE это требуется для навигации по проекту
set(SOURCES
  file1.c
  file2.cpp
  file3.cpp
)

# добавление цели для сборки - бинарного файла;
# синтаксис ${...} означает использование значения
# переменной, которая в данном примере будет раскрыта
# в список файлов, из которых собирается программа
add_executable(my_cool_program ${SOURCES})

Для сборки CMake-проекта необходимо выполнить две стадии:

  1. Сгенерировать Makefileиз CMakeLists.txt
  2. Собрать проект обычнычным инструментом make.

Обычно в процессе генерации Makefile и при сборке проекта создается много временных файлов. По этой причине сборку принято проводить в отдельном каталоге, - чтобы не засорять каталог с исходными текстами.

$ mkdir build     # создаем каталог для сборки
$ cd build        # переходим в него
$ cmake ../       # генерируем Makefile
                  # аргумент cmake - это каталог, который
                  # содержит файл CMakeLists.txt
$ make            # запуск компиляции                   

Использование сторонних библиотек, для который есть готовое описание CMake

Для многих OpenSource библиотек в стандартной поставке CMake уже готовы модули поддержки, которые выполняют поиск библиотеки. В случае c UNIX этот поиск осуществляется с помощью запуска команд конфигурации, либо проверки различных вариантов написания имен файлов в /usr/include и /usr/lib. Для Windows просматривается системный реестр.

Список поддерживаемых библиотек можно найти в поставке CMake, для Linux это может быть каталог (в разных дистрибутивах они разные) /usr/share/cmake/Modules. Все файлы модулей имеют название FindИМЯБИБЛИОТЕКИ.cmake.

Подключение библиотеки, которая поддерживается "из коробки", осуществляется с помощью команды find_package. В случае, если необходимые файлы присутствуют, то определяются переменные:

  • ИМЯБИБЛИОТЕКИ_FOUND - переменная, значение которой устанавливается в 1, если библиотека не отмечена как REQUIRED;
  • ИМЯБИБЛИОТЕКИ_INCLUDE_DIRS - список дополнительных каталогов, в которых нужно искать заголовочные файлы (опции компилятора -I...);
  • ИМЯБИБЛИОТЕКИ_LIBRARIES - список дополнительных библиотек и каталоги к ним (опции компилятора -l... и -L...);

Пример для curl:

# найти библиотеку CURL; опция REQUIRED означает,
# что библиотека является обязательной для сборки проекта,
# и если необходимые файлы не будут найдены, cmake
# завершит работу с ошибкой
find_package(CURL REQUIRED)

add_executable(my_cool_program ${SOURCES})

# добавляет в список каталогов для цели my_cool_program, 
# которые превратятся в опции -I компилятора для всех 
# каталоги, которые перечислены в переменной CURL_INCLUDE_DIRECTORIES
target_include_directories(my_cool_program ${CURL_INCLUDE_DIRECTORIES})

# для цели my_cool_program указываем библиотеки, с которыми
# программа будет слинкована
target_link_libraries(my_cool_program ${CURL_LIBRARIES})

Если необходимо использовать библиотеку для всех целей проекта, а не для отдельных, то можно использовать команды include_directories и link_libraries.

Использование сторонних библиотек, для которых есть описания pkg-config

Для многих GNU-библиотек существуют описания их использования, которые можно использовать утилитой pkg-config(1). Эти описания можно использовать в проектах CMake в том случае, если для них не реализованы описания CMake, но существуют описания pkg-config. Обычно эти файлы *.pc располагаются в каталоге /usr/lib[64]/pkgconfig.

Пример для fuse3:

# подключаем модуль интеграции с pkg-config
find_package(PkgConfig REQUIRED)

pkg_check_modules(
  FUSE         # имя префикса для названий выходных переменных
  REQUIRED     # если библиотека является обязательной
  fuse3        # имя библиотеки, должен существовать файл fuse3.pc
)

# можно использовать переменные FUSE_INCLUDE_DIRECTORIES 
# и FUSE_LIBRARIES
target_include_directories(my_cool_program ${FUSE_INCLUDE_DIRECTORIES})
target_link_libraries(my_cool_program ${FUSE_LIBRARIES})

# дополнительные флаги компиляции, например определения -D...,
# которые не указывают на каталоги для поиска заголовочных файлов,
# перечислены в переменной FUSE_CFLAGS_OTHER
target_compile_options(my_cool_program ${FUSE_CFLAGS_OTHER})

Использование сторонних библиотек, для которых вообще ничего нет

В случае, если для библиотеки не подготовлено никаких описаний, то можно попытаться найти необходимые файлы, используя перебор различных комбинаций имен и стандартных каталогов:

# поиск файла динамической библиотеки
find_library(
  SOME_LIBRARY     # переменная, в которую будет записан результат
  NAMES            # перечисляются различные варианты имен для поиска
    some
    something
    somelib0
)

# поиск пути к заголовочным файлам
find_path(
  SOME_INCLUDE_DIRECTORY  # переменная, в которую будет записан результат
  NAMES                   # имена файлов, которые могут содержаться в каталоге
    somelib.h
    somelib_common.h
  PATH_SUFFIXES           # возможные имена подкаталогов в .../include/
    somelib
    somelib-1.0
)

target_include_directories(my_cool_program ${SOME_INCLUDE_DIRECTORY})
target_link_libraries(my_cool_program ${SOME_LIBRARY})