Контроль сторонних библиотек с помощью Subversion.

December 29th, 2006
Я потратил пол часа, чтобы перевести фразу “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. Так что такую схему можно смело использовать для хранение больших библиотек.

  1. rs
    December 30th, 2006 at 11:00 | #1

    я не сильно много жил с subversion, потому вопрос – для данного метода, насколько сложно будет держать в дистрибутиве актуальные версии действительно больших библиотек (например boost или wxWidgets) если я обновляю их с репозиториев соответствующих проектов (некоторые вещи из того же boost-а хочется пользовать до того, как выйдет релиз в котором они появятся)?

  2. Not a kernel guy
    December 30th, 2006 at 12:39 | #2

    Я ещё не пробовал перенести boost в свой репозиторий. А все остальные библиотеки достаточно малы, чтобы делать миграцию руками. Однако этот процесс несложно автоматизировать с помошью tailor.py: http://www.darcs.net/DarcsWiki/Tailor

    Пример синхронизации SVN и darcs можно найти здесь: http://www.darcs.net/pipermail/darcs-users/2005-January/005070.html. Синхронизация SVN и CVS должна работать аналогично.

    Со временем я собираюсь перенести boost в свой репозиторий. Тогда я смогу поделиться детальным HOWTO.

  3. Sobkovych Alexandr
    January 17th, 2007 at 06:17 | #3

    Мы у себя решили попробовать использовать SVN вместо CVS и сейчас размышляем над тем как удобнее спланировать структуру хранилища.
    Если не трудно уточни некоторые моменты по статье.

    Исходники (или бинарные файлы) библиотек копируются в каталог проекта, причём всегда копируется trunk нужной версии, а не одна из помеченных версий.

    Под каталогом проекта подразумевается каталог в хранилище?
    Создается ли при такой схеме новая метка версии для проекта при создании новой версии библиотеки?

  4. Not a kernel guy
    January 17th, 2007 at 10:02 | #4

    > Под каталогом проекта подразумевается каталог в хранилище?

    Да.

    > Создается ли при такой схеме новая метка версии для проекта при создании новой версии библиотеки?

    Обычно да, если проект переводится на новую версию библиотеки. Т.е. если делается “svn merge -r N:M project/trunk/library-x”. Нет, если новая версия библиотеки просто добавляется в репозиторий и ничего больше не происходит.

  5. Not a kernel guy
    January 17th, 2007 at 10:11 | #5

    > Однако этот процесс несложно автоматизировать с помошью tailor.py: http://www.darcs.net/DarcsWiki/Tailor

    Похоже, что этот скрипт не очень подходит для этой цели. Он переносит все изменения один-в-один, включая все промежуточные версии. В нашем же случае нужны только финальные версии…

  1. No trackbacks yet.
Comments are closed.