Контроль сторонних библиотек с помощью Subversion
Dec 29, 2006 · CommentsИнструменты
Я потратил пол часа, чтобы перевести фразу “managing project dependencies” на русский и всё равно вышло коряво…
Когда я начинал пользоваться Subversion, по старой CVS-ой привычке не мог привыкнуть к тому, что в Subversion “всё, буквально всё,” делается копированием. “Всё” – это и копирование само по себе и создание веток (branches) и меток (tags). Кстати, отсутствие меток в чистом виде я не понимаю до сих пор. Возможно с архитектурной точки зрения это правильно, но с точки зрения пользователя это не удобно – поставив метку пользователь должен озаботится защитой вновь созданной ветки от изменений. Это можно сделать на уровне контроля доступа к репозиторию, но всё равно неудобно.
Вернёмся к нашим баранам.
Со временем я оценил универсальность копирования. Сегодня я бы хотел поделиться удобным способом управления сторонними библиотеками, используемыми в моих проектах. Итак классический набор проблем, связанных с использованием сторонних библиотек:
-
Сторонние библиотеки требуют хотя бы минимальной настройки окружения. Это может быть переменная окружения или путь в списке include директорий;
-
Часто код зависит от конкретной версии библиотеки, что опять же добавляет работы по настройке окружения;
-
Помимо первых двух пунктов, любая сторонняя библиотека требует хотя бы минимальной установки (например нужно распаковать архив);
-
В случае, когда код библиотеки открыт для модификации, в него зачастую вносятся локальные исправления (особенно если разработчик библиотеки не радует частыми релизами). Подобные изменения ещё долее усложняют инсталляцию и настройку окружения;
-
Переход на новую версию библиотеки влечёт за собой ещё больше проблем с обновлением библиотеки на всех компьютерах.
В качестве решения этих проблем удобно помещать исходный код или готовые бинарные файлы в репозиторий вместе с исходниками проекта. Конечно, в идеальном случае, в репозитории не должно быть бинарников, однако жизнь такая сложная штука… Как правило, я использую вот такую структуру репозитория:
/
external
library-1
trunk
version-1
version-2
library-2
trunk
version-1
version-2
project-1
trunk
library-1
library-2
version-1
version-2
project-2
Все стороние библиотеки помещаются в “/external”. Для каждой создается отдельный каталог. Текущая версия библиотеки хранится в “/external//trunk”. Все последовательные версии – в соответствующих метках “/external//version-x”. Проекты организованы аналогичным образом. Исходники (или бинарные файлы) библиотек копируются в каталог проекта, причём всегда копируется “trunk” нужной версии, а не одна из помеченных версий. Поскольку речь идет о копировании в понимании как это сделано в Subversion, такая схема позволяет относительно безболезненно мигрировать на новую версию библиотеки. Полный список преимуществ:
-
Отпадает необходимость настройки окружения. Все нужные файлы берутся из репозитория;
-
Каждый проект может пользоваться конкретной нужной ему версией библиотеки все зависимости от других проектов;
-
Миграция на новую версию библиотеки безболезненна насколько это возможно;
-
Локальные изменения в коде библиотеки будут учтены при смене версий.
Переход на новую версию библиотеки происходит в три этапа:
-
Новая версия файлов чекинится в “/external/*/trunk”. При этом важно не забыть про добавленные и удаленные файлы. Это самый трудоёмкий этап;
-
После того, как все файлы были зачекинены, создается новыя метка “/external/*/version-X”;
-
В каталоге проекта делается merge, чтобы получить свежие изменения из “/external/*/trunk”.
В заключение добавлю, что помимо перечисленных достоинств, репозиторий не распухает от многочисленных копирований каталогов в силу особенностей реализации копирования в Subversion. Так что такую схему можно смело использовать для хранение больших библиотек.