Капелька рефлексии для С++. Часть вторая: публикация на GitHub

2f074cc78e8547d48ab5814d7082bff3.bmp

Эта статья является второй в цикле о библиотеке cpprt, предназначенной для добавления и использования минимальной метаинформации о классах С++.

В отличие от первой статьи, здесь почти ничего не будет о самой библиотеке cpprt. Я постарался подробно и максимально абстрагируясь от своей библиотеки изложить историю оформления библиотеки для её цивилизованной публикации на GitHub.

В статье затрагиваются вопросы лицензирования, структуры проекта и достаточно много внимания уделяется CMake.

Cсылки на все статьи цикла

0. Вступление

Чтобы оттянуть необходимость говорить какие-то вступительные слова, представлю структуру данной публикации:

Раздел №0. Данный раздел. Описание структуры публикации и вступительные слова.
Раздел №1. Немного о лицензировании.
Раздел №2. Немного о создании отдельного git-репозитория подпроекта из git-репозитория основного проекта.
Раздел №3. Мысли о структуре проекта вообще и попытки анализа структуры нескольких существующих репозиториев.
Раздел №4. Уроки CMake на базе хроники разработки cmake-конфигурации для проекта cpprt.
Раздел №5. Несколько слов и много ссылок по поводу документации.
Раздел №6. Краткий раздел с мыслями о продвижении библиотеки.
Раздел №7. Заключение и титры.

Так. Структура есть и по-прежнему нужно с чего-то начать… Ладно, давайте так.

Цель статьи: Рассказать о том, как я готовил библиотеку к публикации на GitHub.

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

Я прекрасно отдаю себе отчёт в том, что репозиторий, как и статья, далеки от идеала. Пишите в личку, пишите комментарии, смотрите библиотеку, коммите в библиотеку — я не забуду упомянуть автора внесённых предложений в титрах к статье. Каждая ваша правка поможет людям, которые будут идти в мир открытого ПО по вашим стопам!

Много пафоса нагнал… Ладно, теперь чуть истории. В первой статье цикла я упоминал, что классы и макросы библиотеки cpprt использовались в рамках моего большего проекта. Они не были выделены отдельно и жили просто как часть исходников. Но в какой-то момент я заметил, что у данных классов почти нет зависимостей от основного проекта, а их функционал самодостаточен и применим для некоторых типичных задач.

И вот тогда-то я задумал выделить часть проекта в отдельный подпроект и осуществить давнюю мечту: опубликовать библиотеку с открытым исходным кодом.

Перед тем как читать дальше, вам, вероятно, будет интересно ознакомиться со следующими мыслями (если вы не читали первую статью): признание в истинных коварных мотивах данных публикаций (ссылка).

И ещё два предупреждения читающему:

Дисклеймер о лени

Данная статья практически не содержит моих мыслей. Это некоторая компиляция разнообразных источников. Всё дело в том, что если первая статья цикла касалась непосредственно разработки (процесса творческого), то здесь рассказывается о каких-то принятых стандартах и методиках, которые должны чётко регламентироваться и, желательно, иметь как можно меньше самодеятельности. Отсюда небольшое количество собственных мыслей и обилие ссылок.


Замечание про терминологический хаос

Хочу также сразу признать некоторый терминологический хаос, царящий в статье. «Солюшены», «пути поиска хедеров», «цели сборки» и прочие… Я пробовал понять как принято именовать все эти понятия по-русски, но так и не нашёл приемлемой системы именования. Пишите в личку, либо в комментарии, объясню если какой-нибудь термин не ясен и внесу правки в именования, если подскажите какую-нибудь общепринятую терминологию.

Если мне так и не удалось отбить у вас желание читать сей опус — поехали!

1. Выбор лицензии

24ae32c5fc3545e396ee396eba85d365.png

Есть одна штука, которая независимо от качества кода делает исходники весомее в моих глазах. Возможно, кому-нибудь это покажется смешным, но для меня это шапка с указанием лицензии. Вот смотрю: есть шапка — значит проект действительно продвинутый. Нет шапки — не так круто. Это как-то автоматически работает, на подсознании.

Поэтому когда я решил опубликовать библиотеку, первым делом руки чесались включить в неё информацию о лицензировании в исходники. А для этого, в свою очередь, нужно было выбрать лицензию.

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

На практике всё оказалось не так страшно — во всяком случае, если говорить о лицензиях для открытого ПО (с проприетарными лицензиями я пока не разбирался). Единственное, что важно помнить при выборе — так это то, что подобные лицензии бывают двух типов: open source и free software. Звучит похоже, но между ними есть, как говорят в Одессе, две большие разницы. Разберёмся подробнее:

Open source. Осторожно, open source «open» неспроста! Open source превращает в open source любой код, который его использует — и при этом исходники любого open source кода должны быть открыты по первому требованию.
Вот вы, например, используете в своём проекте библиотеку под какой-нибудь open source лицензией. Используете год, используете два… И вдруг у вас просят, чтобы вы показали весь свой код. И вы не имеете права отказаться! Принудительный IT-коммунизм. «От каждого по исходнику, каждому по исходнику». Если вас подобная перспектива пугает — следите за лицензиями проектов, которые подключаете.
Набор пунктов, требующих открывать код в рамках семейства лицензии open source, имеет своё название: копилефт. Основной лицензией, включающей в себя копилефт, является лицензия GPL (General Public License) и родственные ей.
Преимущества: Полная открытость кода позволяет повысить его качество за счёт более широкой аудитории читателей и ускорить его разработку за счёт более широкой аудитории писателей. Ну и, конечно, ещё одно преимущество — вы чувствуете, что приносите миру добро. Make open source, not war!
Недостатки: Ограничена возможность применения продуктов под GPL-лицензией в проприетарном коде и/или в коде, содержащем коммерческую тайну. Какая ж это тайна, если о ней нужно рассказать по первому требованию?

Free software. Лицензии free software разрешают пользователю делать с кодом что угодно без каких-либо ограничений, иногда с какими-нибудь чисто условными требованиям (например, с обязательством указывать автора оригинальной библиотеки). Одной из таких лицензий является, например, лицензия MIT. Сюда также входит семейство лицензий BSD.
Преимущества: Дополнительные качественные коммиты от щедрых коммерческих компаний.
Недостатки: Жадные компании могут утащить код себе, форкнуть в корпоративный репозиторий и тихонько развивать его только для себя, не делясь с сообществом.

Library public license. Особняком среди лицензий для открытого исходного кода стоит лицензия LGPL (Library General Public License) и родственные ей. Данный тип лицензий включает в себя признаки как open source, так и free software.
LGPL позволяется свободно использовать код, скомпилированный в бинарное представление (исполняемый файл либо динамическую библиотеку) где угодно, в том числе в ваших коммерческих проектах без ограничений и без требования открывать ваш код.
Но если вам захочется статического связывания исходников проекта под лицензией LGPL с кодом своего проекта — добро пожаловать в мир open source со всеми его плюсами и минусами. Извольте делиться кодом своего проекта.
В качестве примера проекта, использующего LGPL, можно привести Qt. За счёт LGPL-лицензирования его динамическими библиотеками можно пользоваться в проприетарном ПО без ограничений (обсуждение этого на официальном форуме Qt и аналогичное обсуждение на сайте qtcentre).
Преимущества: Косвенная заинтересованность в качестве кода коммерческих организаций (им же хочется, чтобы код собранной библиотеки работал хорошо) и, одновременно, запрет на развитие LGPL-проекта «втихую», как это возможно с лицензиями free sofrware.
Недостатки: Чтобы использовать библиотеку под LGPL лицензией в проприетарном ПО, придётся самому организовывать связывание своего продукта с собранной в dll (либо в so) библиотекой, что потребует пусть небольших, но всё же усилий.

Если знаете про какие-нибудь ещё интересные лицензии — можете написать о них в комментариях. Думаю, это всем будет интересно.

Изучив существующие лицензии, я остановил свой выбор на лицензии MIT, как на одной из самых либеральных лицензий. Среди данных ссылок в конце данного раздела есть несколько про лицензию MIT. Там ней рассказано достаточно хорошо, я лучше не буду пересказывать, чтобы не искажать суть.

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

1. Положил в корень репозитория файл licence.txt (либо LICENCE.txt, чтобы заметнее было) с текстом лицензии, который скопировал вот с этого сайта.
2. Во все файлы исходного кода добавил шапку следующего вида:

Пример лицензионной шапки
////////////////////////////////////////////////////////////////////////////////
//
//
//
// Copyright © ()
//
// Distributed under (See accompanying file LICENSE.txt or copy at
//)
//

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

Закончу раздел я, как и обещал, списком ссылок на интересные материалы:

Полезные ссылки
1. Вот эту статью я читал первой. 2002 год, ага, статья невероятно старая, но, вроде, более или менее актуальная, а в самой статье есть удобная сравнительная табличка для шести разных open source и free software лицензий. Если такой таблички мало — можно глянуть более мощную табличку на википедии.
2. Про основные open source лицензии в одной картинке.
3. Про лицензии откытого ПО чуть подробнее.
4. Про open source лицензии совсем подробно. Информация про лицензию MIT в рамках той же статьи.
5. Совсем подробно про MIT-лицензию, с историческими экскурсами.
6. Подробная статья про MIT-лицензию. Помимо этого, интересна некоторыми мыслями про открытое ПО вообще.
7. Очень подробная статья с философией от GNU-сообщества на тему разницы между free software и open source.
8. Любопытная статья о том, что не всё так просто в мире открытого ПО.
9. Статья со смелой и интересной идеей: создать лицензию, требующую открывать не исходный код, а тесты и результаты работы тестов.
10. Вопрос, который я когда-то задавал на тостере (забавно его было перечитать сейчас, в марте 2016-ого года). В этом вопросе пользователь с ником @littleguga посоветовал мне лицензию MIT, которой я, в конце концов, воспользовался.

2. Отцепление кода от основного проекта

fcf2a6140a364fb0899826a5a1b15d52.png

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

К счастью, всё не так печально. В подобных случаях можно использовать механизм git subtree split. Я научился этому трюку вот тут (ещё тут упоминается, а вот рассказ об альтернативном методе, filter-branch). Если будет интересно, я могу как-нибудь подробнее рассказать о том, какие приключения пережил с этим каменным молотом в руках, раскалывая монолитные репозитории на подмодули и склеивая из них разные штуки с помощью submodule. Здесь же я лишь коротко опишу, как можно переселить историю редактирования какой-то отдельной папки основного репозитория в отдельный репозиторий:

1. cd {main-repo-path}

Для начала нужно перейти в корень основного репозитория (того репозитория, от которого мы будем отцеплять папку).


2. git subtree split -P {submodule-relative-path} -b {submodule-branch}
{submodule-relative-path} — путь, относительно корня репозитория, к папке, которую будем изолировать в ветку.
{submodule-branch} — ветка, в которую скопируется вся история работы с исходниками папки, расположенной по пути {submodule-relative-path}.

После вызова этой команды в локальный репозиторий добавится ветка {submodule-branch}, содержащая все коммиты, связанные с изменениями в папке {submodule-relative-path}. Вызов данной команды лишь создаст новую ветку и скопирует в неё коммиты, ничего другого изменено не будет.

Замечание: Стоит отметить, что коммит-месседжи, привязанные к скопированным коммитам, останутся не изменёнными. Они могут сохранить лишнюю для истории редактирования папки информацию (в случае если в каких-то коммитах были изменения и в папке и вне её). Я не знаю, как этого избежать и не думаю, что можно вообще как-то этого избежать.


3. git push {submodule-repo-URL} {submodule-branch}:{master}
Push для новой ветки в другой репозиторий.

{submodule-repo-URL} — URL ремоут-репозитория, в который мы хотим поселить историю изменений папки.
{master} — имя, которое получит ветка {submodule-branch} в ремоут-репозитории {submodule-repo-URL}. Если это первая ветка, добавляемая в репозиторий, то, подчиняясь древней традиции именования главных веток git, лучше всего называть её «master».

После вызова этой команды, в репозиторий, расположенный по URL {submodule-repo-URL} будет добавлена новая ветка с именем {master}, содержащая в себе историю изменений папки {submodule-relative-path}.


4. git branch -D {submodule-branch}

Если вам больше не нужна ветка с историей редактирования папки {submodule-relative-path} — стоит удалить эту ветку. -D-большая означает, что мы форсим удаление ветки. Если удалять ветку через -d, то git будет ругаться: мол, мы данную ветку никуда не пушнули перед удалением (git чего-то не отстреливает, что мы её на самом деле пушнули в другой репозиторий).

В общем, всё. Проделав описанные шаги для своего основного проекта, я изолировал историю редактирования исходников будущей библиотеки cpprt, получив таким образом очень черновой репозиторий и сохранив всю историю изменений её файлов (пусть и с некоторым мусором в коммит-месседжах).

Репозиторий был готов… Но до публикации было ещё далеко. Ещё полагалось привести репозиторий к подобающему виду. Для этого одних исходников и лицензии было мало. Нужно было озаботиться созданием кучи сопроводительных материалов: тестов, примеров, продумать механизм сборки всего что умеет собираться, создать всякие read me файлов, документации, и т.д… Для всего этого следовало продумать структуру репозитория. Следующий раздел статьи посвящён структуре проекта.

Полезные ссылки:
1. Вопрос, который я задавал когда-то, выбирая между submodule и subtree. Там есть достаточно длинное обсуждение и много ссылок (часть из них я уже использовал выше). Возможно, тем, кто интересуется темой модульной организации проекта, может быть интересно почитать дискуссию и полазить по ссылкам. Кстати, хочу, воспользовавшись случаем, поблагодарить пользователя Andy_U за активную дискуссию в обсуждении этого вопроса — несмотря на то, что я, в конце концов, выбрал submodule.
2. Ещё один вопрос, в котором я описал трудности, с которыми столкнулся при попытке работы с subtree… Кстати, возможно, кто-нибудь сможет объяснить что я делал не так?

3. Мысли по поводу структуры репозитория

83e92243e72749aa90261327270aa795.png

Итак, структура репозитория… На мой взгляд, это нечто из разряда архитектуры. Архитектура кода задаёт принципы взаимодействия его элементов друг с другом. Архитектура — она же структура — репозитория вашего проекта задаёт принципы взаимодействия с ним других проектов. Под понятием «другие проекты» я понимаю здесь не только кодобазы, системы сборок или бизнес процессы. Я имею в виду также людей, которые будут пользоваться вашим кодом и будут вносить в него свои изменения.

Я попробовал в силу своего понимания сформулировать требования к проекту с точки зрения пользователя и с точки зрения контрибутора.

Взгляд с позиции пользователя библиотеки

Перечисление требований в порядке их возникновения при знакомстве с библиотекой:
1. Хочу понимать, с чем имею дело. Какой-нибудь ридмик с кратким описанием о чём это в корне проекта, и чтобы он отображался на странице проекта в GitHub. Ну и, конечно, документация. Желательно, с картинкам.
2. Я хочу глянуть как работать с кодом. Тесты, примеры использования кода, и чтобы при этом было понятно, как оно собирается.
3. Про сам проект… Хочется тратить минимум нервов и времени на сборку, настройку и интеграцию. Было бы круто, если бы имелась папка с хедерами и собранная библиотека для моего компилятора и моей платформы. Подключу, слинкую — и в продакшн и не хочу я разбираться в этой вашей билд-системе.
Примечание: Тут и дальше речь идёт о специфических для С++ вещах. Для прочих языков всё несколько проще… Наверно.
4. У меня платформонезависимый проект! Я хочу, чтобы была возможность собрать библиотеку самому и без необходимости подстраиваться под вашу систему сборки. Если говорить по-другому, мне нужен CMake (если не ясно, что такое CMake — ничего страшного, о нем будет сказано дальше)!
Дополнительные требования:
5. Так, ваша библиотека содержит слишком много возможностей. Можно какие-нибудь тулзы в поставке? И желательно, чтобы сразу собранные.
6. Я уже давно использую ваш проект. Слышал, он обновился недавно? Хочу знать, что изменилось.

Итого, с учётом представленных требований, получается следующая структура:

Про нотацию записи файловых деревьев
В статье используется следующая нотация для описания файловых деревьев:

file_in_root
Файл, находящийся в корне дерева файлов. К любому элементу файлового дерева могут быть добавлены комментарии. Данный текст является комментарием к файлу file_in_root. Комментарий пишется под записью файла/папки, либо, если помещается в одной строке, справа от записи.

/folder_in_root
Папка, находящаяся в корне дерева файлов.

-file_in_folder
Файл, находящийся в некой папке. Чтобы понять, в какой папке лежит файл, нужно глянуть элементы файлового дерева выше. Количество дефисов задаёт уровень вложенности. В данном случае один дефис означает, что файл лежит в какой-то папке, которая, в свою очередь, находится в корне файлового дерева.

-/folder_in_folder
Папка, находящаяся в другой папке, лежащей в корне файлового дерева.

1. Что за библиотека?
README.txt — короткое описание проекта.
/doc — тут лежат файлы, детально описывающие проект и его API.

2. Тесты и примеры.
/tests — тестовые проекты, которые максимально просто запустить.
/examples — примеры, которые тоже должны запускаться как можно проще.

3. Мне бы быстренько подключить…
/include — интерфейс для доступа к API собранной библиотеки.
/lib — сборка исходников в статическую библиотеку.
/bin — сборка исходников в динамическую библиотеку.
Примечание: Папки lib и bin, желательно, должны содержать сборки основных компиляторов и платформ.

4. Хочу собрать сам!
/src — исходные коды проекта.
cmake_readme.txt — опциональная информация о том, как работать с cmake-файлами для генерации конфигураций проекта.
CMakeLists.txt — файл с конфигурацией сборки.

5. Инструменты? Давайте вот сюда:
/tools — тулзы, облегчающие работу с библиотекой. Либо собранные, либо в виде исходников.

6. Обновления
change_list.txt — информация о последних изменениях в проекте.

Взгляд с позиции контрибутора библиотеки

Перечисление требований в порядке их возникновения при знакомстве с библиотекой:
0. Скорее всего, на первом этапе знакомства с библиотекой я не контрибутор — я пользователь. Поэтому, как минимум, в мои требования входят указанные выше требования пользователя.
1. Мне нравится ваш проект, и я хочу его развивать. Я хотел бы почитать какой-нибудь ридмик по поводу того, как влиться в дружную семью контрибуторов.
2. Я хочу понять, как устроена библиотека изнутри. Мне нужна углублённая документация для разработчиков. В идеале документация нужна также к системе сборки проекта.
3. Я не хочу лазить по всему проекту при работе с билд-системой. Если сборка требует какого-то обилия конфигурационных файлов, либо дополнительных программ — пусть лежит в отдельной папке… Вообще, во всём должен быть порядок, файлы должны быть сгруппированы по назначению.
Дополнительные требования:
4. Пользовательские тулзы — это, конечно, хорошо… Но проект реально большой. Нужны какие-то инструменты и для разработчиков.

Итого, с учётом требований, получаются следующие уточнения структуры идеального проекта:

1. Вводная информация:
contributors_README.txt — краткое описание проекта для желающих вложить силы в его развитие.

2. Документация:
/doc
-/developer_doc — документация с деталями реализации проекта.
Примечание: При такой структуре пользовательскую документацию стоит также отложить в отдельную папку в рамках папки doc (в какую-нибудь user_doc). Такая организация документации, по моей задумке, помимо структурной упорядоченности, интриговала бы любопытного читателя доки поглядывать: «что же там внутри доки для посвящённых?» — и, таким образом, делала бы читателя вероятным контрибутором.

3. Порядок в проекте:
/build
Папка с билд-системой. Желательно, чтобы вне этой папки по проекту болталось минимум файлов билд-системы.

4. Инструменты для разработки:
/tools
-/developer_tools
Аналогично документации, собрать тулзы по их назначению: пользовательские отдельно, разработчиские отдельно.

Перечисленные здесь требования касаются субъективно моего восприятия. Если у вас есть какие-нибудь свои мысли по поводу — поделитесь.

Во время анализа проектов с GitHub я заметил, что в них часто упоминаются файлы для работы с утилитой CMake. Больше того, судя по некоторым материалам, именно CMake повлиял на формирование классической структуры репозитория открытого кроссплатформенного ПО на С++. Назревало чувство, что его не обойти. Предстоял…

4. Путь к познанию CMake

fc03bc9f687443159833266a0b06d936.bmp

Благодарности: Спасибо Станиславу Макарову (Nipheris), общение с которым послужило для меня толчком к изучению CMake.

CMake — утилита, позволяющая генерировать конфигурационные файлы конкретных make-систем (и/или проекты некоторых IDE) для сборки С/С++ проектов на основании универсальной, абстрактной конфигурации (список поддерживаемых систем сборки/IDE). После генерирования уже конфигурации и/или файлы проектов, получаемые на выходе из CMake, используются конкретными механизмами сборки; только в результате их работы получается собранный продукт.
Можно сказать, что CMake — это meta-make tool, абстракция над системами сборок C/C++ проектов.

Признаюсь, при формировании структуры репозитория для библиотеки cpprt я всячески увиливал от использования CMake. Типичная прокрастинация… Я придумывал разнообразные отговорки, сочинял свои билд-системы на питоне, батниках и ещё фиг знает на чём, городил какие-то хрупкие конструкции на подмодулях. Апофеозом всего этого безумия стала целая теория, обосновывающая мой отказ от CMake. Мол, так как библиотека cpprt очень маленькая (всего два файла), для её интеграции достаточно вставить исходный код «библиотеки» прямо в проект пользователя как подмодуль. А исходники примеров и тулзов тоже нужно рассовать по подмодулями — чтобы пользователь мог их по желанию подтягивать в репу библиотеки.
Причём, так как у примеров и тулзов есть зависимости от библиотеки cpprt, саму библиотеку (внимание!) нужно тоже встроить в эти подрепозитории как подмодуль. Примеры ведь, должны показывать как полагается встраивать библиотеку в проекты…

Таким образом, на основании такой еретической теории, я сшил репозиторий, словно чудище Франкенштейна, из нескольких мини-репозиториев, соединённых воедино механизмом git. Это было настоящее трешище (можете полюбоваться здесь). И делалось всё это, если отбросить отговорки, с единственной целью — лишь бы не учить CMake.

Но совесть не телевизор, ей звук не выключишь. Натыкаясь на случайные материалы в процессе свободного поиска, я постепенно проникался осознанием: CMake — один из столпов современного мира открытого кроссплатформенного ПО для С++. Делать подобный проект без использования CMake — полная ерунда. Это совсем неправильно, а рассказывать людям о таком решении в статье, помимо прочего, означает учить людей дурному.

Поэтому во вторник, двенадцатого числа месяца апреля, я засел за изучение CMake. К вечеру следующего дня я уже имел работающую CMake-конфигурацию проекта и смеялся над своими страхами. Это оказалось проще, чем я ожидал и очень даже удобно.

Излагаю дальше хронику моего погружения в CMake. Надеюсь, кому-нибудь это поможет.

Подробнее о том, зачем нужен CMake
Здесь я постараюсь рассказать о том, как люди пришли к идее создания CMake. Информация ориентирована, прежде всего, на тех, кто пока смутно понимает, в чём призвание данной утилиты. Если вам это и так очевидно –не читать этот спойлер.

Вначале давайте вспомним, в чём заключается одно из серьёзных отличий С++ от языков, имеющих дело с байткодом (Java, C#, Python, и т.д.)? Верно, С++ собирается в платформозависимые бинарники (здесь платформа = операционная система). Это, с одной стороны, даёт возможность выполнять более тонкие оптимизации кода и делает код, собираемый из плюсов, очень эффективным.

Но, с другой стороны, платформозависимость означает наличие своих тулчейнов (тулчейн = компилятор + линковщик + дебаггер + ещё какие-то утилиты) для каждой из платформ. А так как стандарта, задающего принципы конфигурирования тулчейнов для сборки исходников C++ нет, при создании кроссплатформенного кода возникает необходимость задавать специфические конфигурации сборки проекта для каждого тучлейна и IDE. Как, звучит не очень страшно? Давайте рассмотрим эту ситуацию на реальном примере.

Вот есть автор кроссплатформенной библиотеки с открытым исходным кодом. Он хочет выпустить своё творение в мир, наполненный жутким хаосом, и хочет угодить всем потенциальным пользователям его библиотеки, дав им возможность просто и без лишних настроек собрать как библиотеку, так и всякие сопроводительные проекты.
Допустим, наш автор библиотеки хочет дать возможность собирать библиотеку через Visual Studio. Он добавляет в репозиторий библиотеки соответствующий солюшен, добавляет проекты для сборки тестов, примеров и тулзов, настраивает всю эту красоту — всё отлично, библиотека собирается через студию, тесты-примеры-тулзы тоже.

Но вот незадача — у некоторых пользователей его библиотеки стоит MinGW, который запускается через Eclipse IDE и требует совершенно другого конфигурирования для сборки. Исходники те же, принципы их сборки те же —, но задавать их нужно в рамках другой системы, со своими правилами их описания. И пользователи MinGW+Eclipse недовольны, они не понимают, почему для них конфигурацию сборки не предоставили. Разработчик библиотеки, вздохнув, добавляет файлы проектов и для Eclipse IDE, удовлетворяя таким образом имеющийся запрос… Однако теперь негодуют фанаты NMake с его системой сборки. Нужно и для них конфигурацию писать… А также ещё для нескольких прочих тулчейнов и IDE. Куда ни ткнись — везде напорись.

И самое жуткое, что в случае любых изменений в проекте — добавления или удаления файлов, изменения настроек сборки — нужно вручную менять все эти конфиги и проекты IDE. Нехорошо разработчику библиотеки, очень даже грустно ему становится.

Но, к счастью, есть CMake! Достаточно создать в корне репозитория файл CMakeLists.txt, описать в этом файле с помощью языка CMake универсальную конфгиурацию сборки проекта — и после этого любой пользователь, у которого установлена утилита CMake.exe, сможет сам легко и просто сгенерировать конфиги для конкретной используемой им системы сборки кода (либо файлы проектов для IDE), а дальше выполнить нужную сборку в рамках используемой им любимой билд-системы/IDE самостоятельно. Все счастливы, ура!

Вот зачем нужен CMake. Во имя всеобщего удобства сборки.

P.S.: Что круто, концепция CMake подразумевает не только генерацию конфигов, но и их автоматическое обновление при изменении файла CMakeLists.txt. Это может быть очень полезно при работе с часто обновляемыми репозиторием. Достаточно забрать изменения, после чего не надо ничего настраивать: достаточно просто запустить сборку проекта в рамках вашего тулчейна (или в вашей IDE). CMake самостоятельно обновит конфигурацию перед стартом непосредственно сборки.

На последок — несколько необязательных замечаний. Прячу их под спойлеры.

О распространённом дикарстве в использовании библиотек
После моего знакомства с CMake, выяснилось, что и я сам, и некоторые мои знакомые — причём, некоторые весьма опытные, с опытом работы С++ по пять лет — все мы неправильно используем большинство библиотек с открытым исходным кодом. Почти все кроссплатформенные библиотеки предоставляют простую и удобную возможность CMake-сборки, а мы ищем либо сразу собранную библиотеку, либо, в крайнем случае, готовые файлы проектов для нашей IDE — вместо того, чтобы за десять секунд сгенерировать с помощью CMake проекты для своей IDE, да ещё с возможностью тонкой настройки того, что именно мы хотим собирать (саму библиотеку, тесты, примеры, тулзы, какие-нибудь ещё сопроводительные проекты, и прочее, и прочее).
Эта история распространённого забивания гвоздей микроскопами напомнила мне известную грустную байку о мужике, который изобрёл длинные пакетики с сахаром чтобы было удобнее чай в чашку сыпать, но никто не использовал эти пакетики правильно и мужик от этого покончил с собой.

Помните, каждый раз, когда вы игнорируете возможность использования CMake в открытом ПО для С++, в мире плачет один автор библиотеки. Используйте инструменты правильно.

a82b4cf5c1574dec81574d42066aff15.png


О том, почему 14 стандартов + 1 новый это не про CMake
В связи с CMake читателю, вероятно, вспомнится известная картинка про четырнадцать стандартов и про пятнадцатый, который появляется при попытке связать их в один. Но в данном случае эта картинка будет не к месту. Дело в том, что CMake не нужно встраивать как нечто инородное в сборочную инфраструктуру вашего проекта. Достаточно один запустить CMake, чтобы получать удобные для сборочной инфраструктуры основного проекта файлы конфигурации (или проектов для IDE), после чего уже эти файлы (или проекты) можно будет легко встраивать как нечто, соответствующее существующей организации вашего проекта и целевой платформе.

CMake — это не система сборки. CMake над системами сборки. Он ими правит.

fd63ec5838fc49f1a4c33a98a5288a59.png

Мёду-то сколько, мёду, без единой молекулы дёгтя… Но минусы тоже есть, конечно, и о них мы поговорим на практике.

Думаю, теперь, когда вы знаете что такое CMake, зачем CMake и насколько CMake это хорошо — пришло время с ним познакомиться. Итак, хроника…

Все события, изложенные ниже, действительно имели место двенадцатого апреля 2016 года, во вторник. Восстановлено по истории запросов страниц в моём браузере.

(0:35) Первичный поиск материалов

c08eeb83839c4b8482d8f2aaa62dc10a.png

С чего мы обычно начинаем? Верно, начинаем мы с поиска уроков.

По запросу «cmake tutorial» первая же ссылка вела на официальную доку. В основном я разбирался по ней. Так как cpprt — проект пока очень небольшой, понимания первых пяти шагов доки хватило для описания конфигураций всех возможных целей сборки библиотеки (включая сборки тулзов и примеров).

По запросу «CMake habrahabr» на первой странице Google нашлись три статьи, задуманные как обучающие. Вот эта статья понравилась мне больше всего (кстати, она же выпадает второй по запросу «cmake tutorial»). Хорошая обучающая статья, в чём-то смахивающая местами на перевод доки –, но более лаконичная. Разбита на разделы, в каждом из которых рассказывается о возможностях CMake и даются простые примеры по делу, без лишней шелухи. Я использовал эту статью в качестве дополнительного обучающего материала.

Прочие найденные ссылки
1. Ещё один туториал по CMake. Начало мне очень понравилось — там даже про структуру проекта было весьма внятно и красиво написано. Но после первого небольшого и понятного примера, автор начал резко усложнять примеры, я запутался и решил дальше всё же пользоваться указанной выше статьёй.
2. Небольшой разбор CMake-сборки библиотеки LZ4 (относительно небольшого проекта с GitHub). Возможно, для имевших опыт с CMake разбор весьма неплох –, но меня, как совсем-совсем новичка в этом деле, он отпугнул небольшим объёмом комментариев при большом объёме кода и обилии каких-то специфических для 

© Habrahabr.ru