С момента публикации предыдущей части прошло больше полутора лет, была реализована большая куча фичей, сделано несколько релизов, но не об этом пойдёт речь. Пару дней назад в жизни библиотеки произошло важное событие: она была добавлена в основной репозиторий conan'а (conan-center-index). Об том, как это случилось, что для этого пришлось сделать и что вообще нужно делать, чтобы добавить туда свою библиотеку, и пойдёт речь под катом.
JFrog do the best they can
Для начала — пара слов о том, что есть такое conan (он же конан, он же conan.io) и conan-center-index. Конан — это (ещё один, но стремительно набирающий популярность) менеджер пакетов и зависимостей, ориентированный на C++. Он довольно прост в инсталляции, настройке и имеет несколько несомненных плюсов:
- Он не привязан к какой-то конкретной сборочной системе, компилятору или операционной системе;
- Может использовать распределённую систему репозиториев;
- С помощью artifactory позволяет разворачивать локальные(приватные) проектные/командные репозитории;
- Поддерживает поставку зависимостей как в бинарном (уже собранном) виде, так и сборку их локально из исходников под конкретный профиль;
- Относительно прост в настройке и использовании.
Собственно, с конаном вы просто перечисляете список зависимостей, которые требуются вашему проекту, дальше он всё делает сам. И, при условии, что все настройки (conan profile) для проекта указаны правильно, дальше вы просто собираетесь с выгруженными, собранными и настроенными библиотеками. Магия! Но для этого сборки библиотек и рецепты для их on-site-сборки должны где-то лежать. Где-то, где их можно найти. Лежат они, очевидно, в репозиториях. Одном официальном и бесконечном множестве персональных (или командных).
Долгое время официальный репозиторий конана поддерживался основной командой, поэтому пакеты с библиотеками там появлялись очень медленно и неохотно. Гораздо больше можно было найти в репозитории bincrafters — его мейнтейнеры бодро затягивали туда библиотеки и утилиты, так необходимые в повседневной жизни. Но у репозитория bincrafters (как и у любого другого персонального) был один неприятный недостаток: после установки конана его надо было отдельным образом регистрировать.
Прошлым летом было принято решение кардинальным образом изменить сложившуюся ситуацию, и команда conan.io создала проект conan-center-index. Это — гитхаб-репозиторий, совмещённый с мощной сборочной фермой. Любой желающий (ну, почти — об этом ниже) может сделать в этот репозиторий pull request с рецептом библиотеки или утилиты. После прохождения ревью и успешной сборки во всех базовых конфигурациях — библиотека (рецепт сборки, бинарные предсобранные пакеты и всё остальное) попадает в основной репозиторий конана и становится доступной просто по своему имени и версии. Например: jinja2cpp/1.1.0
Nothing to do all day but stay in bed
Кто хотя бы раз слушал мастер-класс Павла Филонова о том, как использовать conan.io в повседневной работе, как добавлять туда пакеты и всё такое, мог получить представление о том, насколько это не сложно. Это действительно так. Освоение этой премудрости занимает ну, может, один рабочий день — как написать простенький рецепт, как создать собственный conan-репозиторий, как опубликовать в нём свой пакет и т. п. В сети можно нарыть немало готовых рецептов для примера, а в репозитории bincrafters есть готовые шаблоны: https://github.com/bincrafters/templates.
Но всё это хорошо и легко ровно до тех пор, пока этот пакет будет использоваться для сборки конкретным тулчейном или довольно ограниченным набором тулчейнов. Если же хочется сделать пакет публичным, разместить его на bintray (хотя бы даже в своём репозитории) — всё становится значительно интереснее и увлекательнее. Я назову одну лишь цифру: 130. При добавлении библиотеки в conan-index (текущий центральный репозиторий конана) делается минимум 130 вариантов сборок этой библиотеки в бинарные пакеты. По крайней мере, именно столько было сделано для Jinja2C++. Сборочная матрица включает в себя:
- Операционную систему (Windows/Linux)
- Архитектуру (x86/x64)
- Компилятор и его версию
- Версию стандарта
- Тип рантайма (для MSVC — это статический или динамический, для gcc — это версия ABI для стандартной библиотеки)
- Конфигурацию сборки (debug/release)
- Тип библиотеки (shared/static)
Относительно хорошая новость здесь в том, что, по идее, такая матрица должна быть уже настроена для CI вашей библиотеки. На appveyor, travis, Github Actions должны крутиться виртуалки, а в виртуалках — докер-контейнеры, которые каждый коммит в репозиторий библиотеки прогоняют через ну, похожу матрицу. Нет? Ещё не крутятся? Не настроены? Ну… Тогда у меня плохие новости.
Errors flying over your head
Аксиома №1: то, что ваша библиотека собирается в вашем локальном сборочном окружении, под конкретный компилятор (или пару его версий) и в паре конфигураций — ещё не значит, что она будет собираться в окружении вашего соседа.
Аксиома №2: если вы не позаботились о достаточном тестовом покрытии вашей библиотеки, то её собираемость ещё не означает, что она будет правильно работать при определённых комбинациях сборочных параметров.
Аксиома №3: вы даже предположить не можете, с каким флагами библиотеку будет собирать кто-то, кто вдруг захотел ею воспользоваться.
Решить основные проблемы со сборкой под кучу конфигураций желательно до того, как принимается решение о публикации библиотеки. Это долго, это нудно, отладка CI может занимать длительное время, требовать кучи коммитов с единственным комментарием: "Debug CI build", но будучи настроенным один раз CI может сэкономить кучу времени и сил.
Приключение это становятся ещё увлекательнее, когда у вашей библиотеки появляются внешние зависимости. Например, от буста или ещё чего-нибудь такого же, не менее популярного. Рассказ об этих приключениях — это тема отдельной статьи. Здесь достаточно лишь сказать, что конан позволяет облегчить жизнь. Если, конечно, речь идёт о пакетах из основных репозиториев (типа conan-index или bincrafters). В этом случае есть определённая гарантия, что если зависимости подходят друг другу по версиям, то, скорее всего, на сборке всё будет хорошо, а лог ошибок сборки будет девственно чистым. Разумеется, в экзотических случаях что-то наверняка пойдёт не так (ведь опция сборки библиотеки из исходников всегда остаётся), но на то они и экзотические случаи. И на этот случай всегда есть баг-трекер проекта, в который можно написать гневный issue.
Для статистики: сборочная матрица библиотеки Jinja2C++ на GitHub Actions состоит из 30-и вариантов сборки под Linux, 48-и сборок под Windows, плюс к этому 16 сборок на appveyor и 5 на трависе. Спойлер: на сборочном конвейере conan-center-index всё равно были проблемы.
If you want to survive, get out of bed!
Итак, решение идти в основной репозиторий конана принято. Что дальше? Тут надо соблюсти несколько предварительных условий:
- Вы совершенно уверены, что библиотека собирается в популярном наборе конфигураций. Под windows, под linux, всем популярным зоопарком компиляторов и прочее. Уверенность будут вселять настроенный и запущенный CI с соответствующей матрицей. Использование сборочной фермы конана для простого тестирования сборки может быть воспринято не очень позитивно.
- У вас на гитхабе (битбакете, гитлабе и т. п.) есть хотя бы один релиз. Из апстрима в conan index ничего не идёт. Только ссылки на архивы исходников с явно прописанными хешами.
- Вы подали заявку на участие в Early Access Programm проекта conan index, и эту заявку одобрили. Этот шаг необходимый, но формальный. Я в соответствующем issue не видел ни одной неодобренной заявки. Заявку подавать здесь: https://github.com/conan-io/conan-center-index/issues/4
- Вы установили себе последнюю версию конана и зарегистрировали хуки к ней. Это необходимое требование. Хуки проверяют ваш рецепт и бьют по рукам, если вы хотите сделать что-то не так. Например, прописали не тот набор настроек, метаинформации, или, скажем, инсталлятор библиотеки копирует лишние файлы (или наоборот — не копирует). Проверок там много.
- Вы форкнули себе репозиторий conan-center-index. Публикация собственной библиотеки делается через PR в этот репозиторий с необходимым и дотошным ревью со стороны мейнтейнеров из conan.io team.
После выполнения этих несложных требований лучше пойти в вики проекта conan-center-index и почитать, как сейчас принято оформлять рецепты: https://github.com/conan-io/conan-center-index/wiki И убедиться, что приключение предстоит несколько сложнее, чем набросать небольшой питон-скрипт и зааплоадить его в персональную репу.
You've got your orders better shoot on sight
Рецепты для конана имеют строго определённый формат, ровно как и расположение файлов рецепта. Рецепт каждой библиотеки лежит в своей подпаке github-репозитория, структура которой зависит от того, что за библиотека и как её можно собирать. Я не буду здесь дублировать содержимое официальной вики, покажу на примере Jinja2C++. Это — собираемая библиотека с пока что одной выпущенной версией. Структура папки с её рецептом такова:
.
+-- recipes
| +-- jinja2cpp
| +-- config.yml
| +-- 1.1.0
| +-- CMakeLists.txt
| +-- conanfile.py
| +-- conandata.yml
| +-- test_package
| +-- CMakeLists.txt
| +-- conanfile.py
В файлах лежит следующее:
config.yml
В этом файле — общая информация о версиях библиотеки, которые лежат в папке проекта и из каких подпапок их собирать:
---
versions:
"1.1.0":
folder: 1.1.0
Дело в том, что есть варианты, когда рецепт пишется один на все версии библиотеки, в этом случае вместо номерных подпапок там будет all, а в config.yml — перечислены все поддерживаемые версии (подробнее об этом — в вики проекта).
1.1.0
Папка, в которой лежит рецепт для сборки этой конкретной версии библиотеки. Или группы версий. Или всех версий. Зависит от. В обязательном порядке содержит файл рецепта (conanfile.py), информацию об источниках исходников (conandata.yml) и подпапку test_package, в которой — рецепт сборки простого тестового проекта. Этот рецепт необходим для того, чтобы убедиться, что пакет с библиотекой в заданной конфигурации собирается корректно.
1.1.0/conanfile.py
Основной файл с рецептом сборки. Подробнее о нём — ниже, в отдельном разделе.
1.1.0/conandata.yml
Файл с информацией об источниках исходных файлов библиотек. Как я писал выше, конкретный рецепт собирается из конкретного набора исходников. Сделано это для воспроизводимости сборки. Это свой собственный персональный рецепт вы можете написать так, чтобы исходники всегда брались из актуального master'а вашего репозитория. Для библиотек в conan-center-index так нельзя. Поэтому пишется такой вот файл:
sources:
"1.1.0":
url: https://github.com/jinja2cpp/Jinja2Cpp/archive/1.1.0.tar.gz
sha256: 3d321a144f3774702d3a6252e3a6370cdaff9c96d8761d850bb79cdb45b372c5
Для каждой версии указывается путь к архиву с исходниками и хеш-сумма этого архива.
1.1.0/CMakeFile.txt
Необязательный, но иногда крайне необходимый файл. В моём случае используется для адаптации некоторых опций сборки, устанавливаемых conan'ом, к CMake-файлу моей библиотеки:
cmake_minimum_required(VERSION 3.4.3)
project(cmake_wrapper)
include(conanbuildinfo.cmake)
conan_basic_setup()
unset (BUILD_SHARED_LIBS CACHE)
if (NOT CMAKE_BUILD_TYPE)
set (CMAKE_BUILD_TYPE ${JINJA2CPP_CONAN_BUILD_TYPE})
endif ()
add_subdirectory(source_subfolder)
Зачем это нужно и что здесь происходит? cmake-генератор конана генерирует специальный файл (conanbuildinfo.cmake), в котором выставляет кучу флагов и настроек сборки, соответствующих текущему сборочному профилю. Ну, там, компилятор, версию стандарта, PIC и прочие подобные вещи. Если у вашего проекта (вашей библиотеки) CMake-файл универсальный, и на conan не заточен, то эти флаги могут легко потеряться. Вот чтобы этого не произошло — и делается скрипт-прослойка. Кроме того, он может делать что-то ещё. В моём случае — прокидывать тип сборки для случая, если сборка толкается на msbuild.exe.
1.1.0/test_package
Подпапка с тестовым проектом. Здесь так же лежит рецепт (conanfile.py) и сборочный скрипт (CMakeLists.txt) и исходник (main.cpp), которые позволяют собраться тестовому проекту вместе с библиотекой и вывести что-нибудь простенькое на консоль. Например, "Hello World!".
1.1.0/test_package/conanfile.py
Рецепт сборки. Так сказать, файл-пускач. Выглядит довольно просто:
from conans import ConanFile, CMake, tools
import os
class Jinja2CppTestPackage(ConanFile):
settings = "os", "arch", "compiler", "build_type"
generators = "cmake", "cmake_find_package"
def build(self):
cmake = CMake(self)
compiler = self.settings.get_safe("compiler")
if compiler == 'Visual Studio':
runtime = self.settings.get_safe("compiler.runtime")
cmake.definitions["MSVC_RUNTIME_TYPE"] = '/' + runtime
cmake.configure()
cmake.build()
def test(self):
if not tools.cross_building(self.settings):
ext = ".exe" if self.settings.os == "Windows" else ""
bin_path = os.path.join("bin", "jinja2cpp-test-package" + ext)
self.run(bin_path, run_environment=True)
В отличие от рецепта сборки библиотеки, этот файл довольно прост. Его задача — толкнуть сборку (метод build
) и толкнуть тест (метод test
). При необходимости — прокинуть дополнительные флаги. Например, тип рантайма для компилятора MSVC. Зависимость от основной библиотеки указывать не нужно — конан это и так знает. Типы генераторов зависят от системы сборки, используемой для сборки тестового проекта. В случае CMake — это cmake
и cmake_find_package
. Что это и зачем — в следующем разделе.
1.1.0/test_package/CMakeLists.txt
Тут всё довольно просто:
cmake_minimum_required(VERSION 2.8.0)
project(Jinja2CppTestPackage)
set(CMAKE_CXX_STANDARD 14)
include (${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
find_package(jinja2cpp REQUIRED)
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
if (CMAKE_BUILD_TYPE MATCHES "Debug" AND MSVC_RUNTIME_TYPE)
set (MSVC_RUNTIME_TYPE "${MSVC_RUNTIME_TYPE}d")
endif ()
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${MSVC_RUNTIME_TYPE}")
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${MSVC_RUNTIME_TYPE}")
set (CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${MSVC_RUNTIME_TYPE}")
set (CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "/PROFILE")
message (STATUS "Build type: ${CMAKE_BUILD_TYPE}")
message (STATUS "USE MSVC RUNTIME: ${MSVC_RUNTIME_TYPE}")
endif()
add_executable(jinja2cpp-test-package main.cpp)
target_link_libraries(jinja2cpp-test-package PRIVATE jinja2cpp::jinja2cpp)
Генератор cmake
, про который я говорил выше, создаёт файл conanbuildinfo.cmake
, а cmake_find_package
— создаёт find'еры, позволяющие искать conan-библиотеки привычным образом. Важный нюанс: имена пакетов — case-sensitive, и иногда (нечасто, правда) в пакетах могут поменять регистр. В этом случае ваш проект внезапно перестанет собираться.
Кроме того, в файле обрабатываются параметры, переданные из рецепта ну и собирается исполняемый файл, который можно запустить.
1.1.0/test_package/main.cpp
Простой исходник, успешная сборка которого говорит о том, что с conan-пакетом всё в порядке:
#include <jinja2cpp/template.h>
#include <iostream>
int main()
{
std::string source = R"(
{{ ("Hello", 'world') | join }}!!!
{{ ("Hello", 'world') | join(', ') }}!!!
{{ ("Hello", 'world') | join(d = '; ') }}!!!
{{ ("Hello", 'world') | join(d = '; ') | lower }}!!!)";
jinja2::Template tpl;
tpl.Load(source);
std::string result = tpl.RenderAsString(jinja2::ValuesMap()).value();
std::cout << result << "\n";
}
Это не полновесный пакет тестов! Это примитивный smoke-тест, не более того. Его задача — собраться и сказать миру "привет!", и всё!
Your finger's on the keyboard
Собственно, рецепт. То, ради чего всё затевается, ради чего всё делается. Это — питон-скрипт, который должен соответствовать стандарту кодирования PEP8. Задача этого скрипта — описать библиотеку с помощью набора метоинформации, а так же дать исчерпывающие инструкции по тому, как, с какими вариантами опций и прочее библиотеку можно собирать. Общая его структура:
import os
from conans import ConanFile, CMake, tools
from conans.errors import ConanInvalidConfiguration
class Jinja2cppConan(ConanFile):
# global settings and metainfo
def config_options(self):
#...
def configure(self):
#...
def source(self):
#...
def build(self):
#...
def package(self):
#...
def package_info(self):
#...
Более подробно о каждой функции можно почитать в документации по conan. Здесь лишь краткая информация:
- config_options — вызывается для корректировки набора опций профиля при сборке под конкретную платформу
- configure — метод для конфигурирования сборки
- source — метод для получения исходников
- build — метод для собственно сборки
- package — метод для формирования пакета из результатов сборки (аналог install, но не совсем)
- package_info — метод, предоставляющий информацию о собранных артефактах для использования в других пакетах и рецептах
Реализация всех этих методов сильно зависит от типа библиотеки, её скриптов сборки и прочих нюансов. В случае с Jinja2C++, которая собирается с помощью CMake-скрипта, реализовано всё было так.
Блок глобальной конфигурации пакета
name = "jinja2cpp"
license = "MIT"
homepage = "https://jinja2cpp.dev/"
url = "https://github.com/conan-io/conan-center-index"
description = "Jinja2 C++ (and for C++) almost full-conformance template engine implementation"
topics = ("conan", "cpp14", "cpp17", "jinja2", "string templates", "templates engine")
exports_sources = ["CMakeLists.txt"]
generators = "cmake", "cmake_find_package"
settings = "os", "compiler", "build_type", "arch"
options = {
"shared": [True, False], "fPIC": [True, False]
}
default_options = {'shared': False, "fPIC": True}
requires = (
"variant-lite/1.2.2",
"expected-lite/0.3.0",
"optional-lite/3.2.0",
"string-view-lite/1.3.0",
"boost/1.72.0",
"fmt/6.1.2",
"rapidjson/1.1.0"
)
_source_subfolder = "source_subfolder"
_build_subfolder = "build_subfolder"
_cpp_std = 14
Он общий для всех пакетов и включает себя собственно метаинформацию (name
, license
, homepage
, url
, description
, topics
), параметры настройки пакета (generators
, settings
, options
, default_options
), список зависимостей (requires
) и глобальные настройки, используемые в методах (_source_subfloder
, _build_subfolder
и _cpp_std
).
Какие тут особенности, связанные именно с пакетами в conan-center-index:
- Считается, что пакет принадлежит всему community, поэтому авторство не указывается (нет параметра
author
); url
всегда указывает на conan-center-index;- Путь к оригинальному репозиторию — в
homepage
; - Библиотека должна поддерживать сборку как static, так и shared-пакетов. Аналогично с -fPIC. Это контролируется hook-скриптами;
- Внешние зависимости — всегда на конкретную версию (диапазоны не допускаются), и только на то, что уже есть в conan-center-index.
config_options
Метод, который позволяет отключить те или иные параметры для определённых конфигураций:
def config_options(self):
if self.settings.os == "Windows":
del self.options.fPIC
В моём случае при сборке под Windows флаг fPIC — не нужен. Поэтому его убираем.
config
Метод конфигурирования сборки пакета. Здесь проверяются параметры профиля сборки, устанавливаются дополнительные флаги и т. п.
def configure(self):
if self.options.shared:
del self.options.fPIC
cppstd = self.settings.get_safe("compiler.cppstd")
if cppstd:
cppstd_pattern = re.compile(r'^(gnu)?(?P<cppstd>\d+)$')
m = cppstd_pattern.match(cppstd)
cppstd_profile = int(m.group("cppstd"))
if cppstd_profile < 14:
raise ConanInvalidConfiguration("Minimum C++ Standard required is 14 (provided '{}')".format(cppstd))
else:
self._cpp_std = cppstd_profile
В моём случае — при сборке shared-версии библиотеки удаляется флаг fPIC и проверяется, что выполняется требование минимального стандарта (14). Если в профиле указано что-то меньшее, то конфигурация считается невалидной. Важно: генерация исключения ConanInvalidConfiguration
— единственный способ корректно инвалидировать конфигурацию с точки зрения сборочной фермы. Все варианты профилей, для которых бросается это исключение, не будут приводить к сбою сборки.
source
Стандартный для всех рецептов метод:
def source(self):
tools.get(**self.conan_data["sources"][self.version])
extracted_dir = "Jinja2Cpp-" + self.version
os.rename(extracted_dir, self._source_subfolder)
Согласно файлу конфигурации здесь выкачивается архив и переименовывается в некое "стандартное" имя.
build
В этом методе выполняется собственно сборка. Используемые средства зависят от того, чем собирается библиотека. Конан поддерживает большой набор сборочных скриптов. Для CMake этот метод выглядит так:
def build(self):
cmake = CMake(self)
cmake.definitions["JINJA2CPP_BUILD_TESTS"] = False
cmake.definitions["JINJA2CPP_STRICT_WARNINGS"] = False
cmake.definitions["JINJA2CPP_BUILD_SHARED"] = self.options.shared
cmake.definitions["JINJA2CPP_DEPS_MODE"] = "conan-build"
cmake.definitions["JINJA2CPP_CXX_STANDARD"] = self._cpp_std
# Conan cmake generator omits the build_type flag for MSVC multiconfiguration CMake,
# but provide build-type-specific runtime type flag. For now, Jinja2C++ build scripts
# need to know the build type is being built in order to setup internal flags correctly
cmake.definitions["JINJA2CPP_CONAN_BUILD_TYPE"] = self.settings.build_type
compiler = self.settings.get_safe("compiler")
if compiler == 'Visual Studio':
# Runtime type configuration for Jinja2C++ should be strictly '/MT' or '/MD'
runtime = self.settings.get_safe("compiler.runtime")
if runtime == 'MTd':
runtime = 'MT'
if runtime == 'MDd':
runtime = 'MD'
cmake.definitions["JINJA2CPP_MSVC_RUNTIME_TYPE"] = '/' + runtime
cmake.configure(build_folder=self._build_subfolder)
cmake.build()
Создаётся нужный объект, через него добавляются параметры сборки, после чего запускается генерация cmake'а и собственно сборка. В моём случае требуется прокинуть CMake-скрипту ряд настроек, специфичных именно для библиотеки Jinja2C++, тип рантайма и тип сборки.
package
Метод, который вызывается для формирования пакета с артефактами сборки. Выглядеть может так, но это совсем не обязательно:
def package(self):
self.copy("LICENSE", dst="licenses", src=self._source_subfolder)
self.copy("*.h", dst="include", src=os.path.join(self._source_subfolder, "include"))
self.copy("*.hpp", dst="include", src=os.path.join(self._source_subfolder, "include"))
self.copy("*.lib", dst="lib", keep_path=False)
self.copy("*.dll", dst="bin", keep_path=False)
self.copy("*.so", dst="lib", keep_path=False)
self.copy("*.so.*", dst="lib", keep_path=False)
self.copy("*.dylib", dst="lib", keep_path=False)
self.copy("*.a", dst="lib", keep_path=False)
По сути — выполняет функцию install-скрипта. В принципе, если ваша библиотека предоставляет собственный сценарий инсталляции — можно использовать и его. Но есть, опять же, нюансы:
- Обязательно требуется файл с лицензией. Это проверяют хуки.
- Все .cmake-артефакты (если ваш инсталлятор их создаёт) должны быть вычищены. Это тоже проверяют хуки.
Поскольку в моём случае инсталляция довольно примитивна — я решил, что проще будет описать её в рецепте, чем обрабатывать напильником результат стандартного install'а.
package_info
Метод, предоставляющий информацию о пакете: его библиотеках, дефайнах, с которыми его надо использовать, и т. п. Чтобы те проекты, к которому этот пакет цепляется, могли корректно собираться. В моём случае реализация примитивна:
def package_info(self):
self.cpp_info.libs = ["jinja2cpp"]
Только библиотека. Раньше ещё define'ы прокидывались, но я смог от этого избавиться.
The sergeant calls (stand up and fight!)
Написать рецепт для пакета с библиотекой — это четверть дела. Надо протащить его через ревью и сборку на ферме. И для этого требуется масса терпения и нервов. Первое, и важное (это даже требуют при заведении PR): вы должны хотя бы один раз запустить сборку пакета локально. То есть хотя бы один раз, на одной из своих сборочных конфигураций выполнить скрипт:
> conan create . jinja2cpp/1.1.0@
(вместо jinja2cpp/1.1.0 — имя вашей библиотеки)
Выполнять эту команду надо в папке, где расположен файл с рецептом. Этот скрипт создаст в локальном conan-репозитории пакет с вашей библиотекой. Всегда с нуля. Если сможет. С первого раза, правда, вряд ли получится. Но… Всякие чудеса бывают.
На самом деле, для серьёзной отладки одного запуска (в одной конфигурации) будет мало. Потребуется тестирование рецепта как на Windows-, так и на Linux-сборках. На Windows — в разных комбинациях флагов shared
и типов рантайма. На Linux… На Linux — проще. В докер-хабе есть докер-образы, которые команда conan использует на своей сборочной ферме. Найти их можно здесь: https://hub.docker.com/u/conanio Лично я использую эти образы на своей сборочной матрице в GitHub Actions.
Для тестирования разных конфигураций сборки полезно будет запастись профилями типа таких:
[settings]
arch=x86_64
arch_build=x86_64
build_type=Release
compiler=Visual Studio
compiler.runtime=MD
compiler.version=15
os=Windows
os_build=Windows
[options]
jinja2cpp:shared=False
[env]
[build_requires]
Чтобы не прописывать всякий раз параметры сборки ручками. Файл профиля конану скормить просто:
> conan create . jinja2cpp/1.1.0@ -p profile_md.txt
profile_md.txt
— это файл с нужным профилем.
И тут может быть ещё одна засада: в процессе отладки сборки под конкретные конфигурации (под какие — вам скажет сборочная ферма, предоставив лог сборки и профиль, который завалился) может потребоваться повторный выпуск релиза. Потому что ошибка может оказаться не в рецепте, а в сборочных скриптах библиотеки, или в исходниках библиотеки. В общем, с момента создания pull request'а в conan-center-index начнётся основная жара и веселье. Которое, так или иначе, закончится, ибо проблемы в конце концов решатся. Иногда — не без помощи команды поддержки conan'а. Например, мне приходилось дожидаться, пока они проапдейтят версии Visual Studio на своих сборочных образах. Ну, всякое бывает, да.
You'll be the hero of the neighbourhood
Да, процесс публикации в conan-center-index непростой, сильно отличается от того, как это рассказывается на презентациях, но, на мой взгляд, оно того стоит. Пакетных менеджеров, достойных внимания, для C++ не так много. conan — один из них, и здесь только от сообщества зависит, насколько он будет в итоге полезным. Например, вот здесь есть длинный список того, что достойно размещения в конане, но ещё не размещено. Сейчас процесс добавления уже неплохо обкатан, можно брать за основу готовые рецепты — и создавать новые.
Ссылки вместо заключения:
Проект conan-center-index
Issue для подачи заявки на участие в EAP
Библиотека Jinja2C++
Профиль библиотеки в conan-center
В тексте статьи в качестве подзаголовков использовались строчки из песни "You in the army now" группы Status Quo
eao197
Допустим, библиотека принята в conan-center-index. Выходит новая версия библиотеки. Я так понимаю, что автор должен оформить этот релиз в своем форке conan-center-index, затем сделать PR для основного репозитория, затем этот PR должен быть принят. И вопрос в том, как быстро этот PR принимается?
Похожая централизованная схема в vcpkg. И там PR может висеть дней до 10. А может и быть принятым в тот же день.
FlexFerrum Автор
Да, всё так. Но вот про скорость принятия не скажу точно. Команда у них небольшая, поэтому может висеть долго. Но можно попинать по альтернативным каналам, чтобы быстрее было.