пятница, 27 ноября 2015 г.

Вектор развития cmake

Давным давно система сборки maven показала, что проект должен описываться легко, не таскать за собой зависимости как хлам управляемый руками и всегда собираться. С тех пор все новые языки программирования первым делом обзаводились подобными системами сборки. Dub, cargo, npm и многие другие. Напиши своё имя, опиши версии библиотек которые ты хочешьь использовать и пиши код. Найти и установить именно те версии именно тех библиотек предсказуемым и надёжным образом это уже дело автоматики. Исходники можно не перечислять, папка в которой они ожидаются по умолчанию мало у кого вызывает раздражение.

Но в мире древней магии всё не так. Cmake поверг autotools но здесь тебе надо на языке с неудобным синтаксисом в императивном коде описывать алгоритм построения графа сборки. Даже поиск зависимостей делается только на половину. После find_package всем просьба поглядеть в документацию по библиотеке которую вы хотите запользовать и узанть, какие директивы прероцессора, необходимые для сборки с использованием оной в каких переменнх будут сложены, а затем вам нужно ручками их включить. Такая же история с исходниками, да и с библиотеками от которых зависит та, единственная, ради использования которой вы яростно боролись с вышеописанным гемором.

Но время неумолимо заставляет двигаться вперёд и, потихоньку, cmake движется в от предоставления кривого скриптового языка описания сборки к описанию проекта. Во первых уже давно можно экспортировать цели, а во вторых на цели можно навешивать свойства, которые автоматам взводят нужные директивы препроцессора и поделючают нужные пути поиска заголовочных файлов. Между целями можно задавать зависимости. Всё вышеописанное позволяет плавно двигаться в сторону вот таких простых и ясных CMakeLists:

cmake_minimum_required(VERSION 3.0)
project(my)

find_package(A)
find_package(B)

add_executable(myprog
  main.cpp
  tools.cpp
  utils.cpp
)
target_link_libraries(myprog
  A::libA
  B::core
  B::net
)

Ненужные технические детали, являющиеся внутренними деталями пакетов A и B болеше не нужно прописывать в своём проекте. Причём библиотек с которыми мы линкуемся, физически, может не существовать. add_library(header_only INTERFACE) с последующим навешиванием требуемых путей для заголовков позволяет описывать через такой же механизм header-only библиотеки.

Следующие интересные вкусности это возиожность экспортировать цели неустановленных библиотек. Тоесть можно выкачать сорцы библиотеки от которой зависит ваш проект в директорию сборки, собрать эту библиотеку и использовать её публичные цели прямо оттуда. Разумеется делать такие вещи руками было бы развлечением для мазохиста, а посему cmake поставляет модуль ExternalProject который позволяет это автоматизировать. Этот модуль, так же, умеет автоматизировать установку этой библиотеки в какую-нибудь поддиректорию директории сборки если проект от которого вы зависите не экспортирует цели из дерева сборки, но, надеюсь, со временем таких проектов будет меньше и меньше.

Итак, сегодняшний cmake позволяет сказать хочу библиотеку A, если она в твоей системе не установлена, то я загружу её из такого-то git/svn/hg/cvs репозитория (либо просто архивом по указанному урлу). И прозрачно пользоваться этой библиотекой только линкуясь с ней, но... Что если хочется выкачать эту библиотеку, поправить её и пособираться с модифицированной версией прежде чем отправить свои патчи в апстрим? И тут тоже уже всё готово (по крайней мере в теории). Существует понятие пользовательского репозитория куда может прописывать себя библиотека, после чего find_package в вашем проете будет подхватывать цели из дерева сборки выкачанной и модифицированной вами библиотеки. Выглядит довольно вкусно.

Внимание! Содержимое предыдущего абзаца уже чистая теория ибо этот функционал cmake'а я пока самостоятельно не опробовал!

И напоследок хочу привести пример рабочего кода. Вот так я описал опциональную зависимость от QJSON в совей библиотеке QRemoteSignal. Немного менее компактно как в системах сборки современных языков, но намного компактней и удобней чем можно было ожидать в мире C++. А главное, вектор развития cmake намекает на то, что когда-нибудь и в C++ проектах зависимости будут управляться автоматически системой сборки с минимальными усилиями. Ну хотя бы лет через 5-10 :)

Комментариев нет: