Проект состоит как из своего кода, так и из библиотек сторонних разработчиков. Естественно возникает вопрос как хранить это добро. На этот вопрос я попытаюсь дать рекомендации на основании собственного опыта.
Основные требования:
- Разработчик должен быстро приступить к работе не забивая голову установкой десятка библиотек.
- Библиотеки должны быть доступны из разных проектов, при этом необходимо избежать дублирования кода в репозиториях.
- Должна быть возможность сохранять определенные состояния проекта: новые версии, номерные сборки для тестеров и т.д.
Перед прочтением настоятельно рекомендую ознакомиться с предыдущей заметкой.
Я рекомендую под каждый проект создавать свой репозиторий. Это позволит четко отделить один проект от другого, при необходимости дать права на редактирование конкретных репозиториев только определенным разработчикам, решить вопрос с архивированием. Библиотеки, даже сторонние, следует рассматривать как отдельные проекты и также хранить в отдельных репозиториях. Исключение может быть сделано только для особо крупных библиотек, например Qt или Boost.
Subversion позволяет установить зависимости между проектами, установив зависимость проекта от других проектов. Таким образом, одновременно с получением проекта из репозитория возможно получить весь необходимый для работы код автоматически, не усложняя жизнь установкой нескольких библиотек и отслеживанием их актуальности.
Папки trunk, tags, branches
Часто в Subversion используют структуру проекта из трех папок: trunk, tags и branches. Для чего они?
- trunk — это папка для разработки. В ней всегда находится самая свежая версия кода, доступная для разработчиков. Здесь крайне важно всегда поддерживать код в собирабельном состоянии. Любая поломка сборки проекта должна исправляться как можно быстрее, чтобы не создавать сложностей остальным разработчикам.
- В tags находятся состояния проекта на определенный момент. Например ближе к релизу, разработчики начинают создавать с определенной периодичностью сборки с уникальным номером которые передаются тестерам. Состояние проекта на этот момент можно зафиксировать в папке tag, присвоив ему говорящее название, например build_723. Это позволит в случае внесения дефекта отсутствующего в данной сборке и обнаруженного в следующей сборке, проанализировать причину проблемы и принять соответствующие меры к ее устранению.Также здесь можно отмечать выпущенные версии, например 1.0.5. Таким образом, получив от пользователей жалобу на ошибку в какой-либо версии, можно взять код актуальный для данной версии, для изучения проблемы.
- branches— временные ветки разработчиков. Что это значит? Разработчик Вася получает задание на добавление некоторой функциональности. Для того чтобы не блокировать работу других членов команды, он создает копию проекта в branches/vasia_task_1234, работает в этой ветке периодически сохраняя код с репозиторий. Выполнив задачу, он проводит слияние с основной веткой разработки (trunk) и удаляет временную ветку. Задача выполнена, при этом он мог фиксировать состояние своей работы во временной ветке, не нарушая работы остальных разработчиков.
Работа с ветками и фиксация состояния проекта в tags будет описана в другой заметке.
Как именовать версии?
Я предлагаю способ нумерации версий состоящий из трех цифр — major.minor.micro, где:
major — цифра обозначающая версию продукта (разные номера версий обозначают существенные отличия между продуктами),
minor — подверсия продукта (разные номера говорят о том, что подверсии обладают разным функционалом),
micro — в рамках подверсии указывает на то, что были исправлены какие либо ошибки.
- При выпуске новой версии содержащей только исправления ошибок увеличивается цифра в позиции micro.
- При выпуске версии с изменившимся функционалом инкрементируется minor и обнуляется micro.
- При выпуске новой версии продукта существенно отличающегося от предыдущего, увеличивается major, а minor и micro обнуляются.
Пример проекта
Рассмотрим простой проект состоящий собственно из кода проекта (project) и двух библиотек (lib1, lib2). Структура из трех репозиториев:
project (Для примера пусть путь к репозиторию будет https://server/project)
branches
tags
trunk
lib1 (https://server/lib1)
branches
tags
1.0
1.5
trunk
lib2 (https://server/lib2)
branches
tags
trunk
Собственно project — это наш проект, который зависит от сторонней библиотеки lib1 версии 1.0 и от библиотеки собственной разработки lib2, которая разрабатывается совместно с самим проектом, поэтому зависимость будет от ее основной ветки разработки (trunk).
Устанавливаем зависимости в Subversion
Для того, чтобы установить зависимости проекта в Subversion используется свойство svn:externals. Для просмотра установленных свойств нужно перейти в папку с проектом (например trunk) и воспользоваться командой
svn proplist . |
Обратите внимание на . — это говорит о том, что надо показать свойства установленные для папки (некоторые свойства можно установить только для конкретных файлов).
Для редактирования и добавления свойств существует команда
svn propedit свойство папка_или_файл |
Для установки свойства svn:externals (естественно для всей папки) находясь в папке с основной веткой разработки (путь_на_локальной_машине/project/trunk) выполните
svn propedit svn:externals . |
После этого Subversion вызовет текстовой редактор где вам надо будет прописать/отредактировать свойства. После сохранения изменений и выхода из редактора, параметры свойства будут изменены. Если изменения не сохранять, свойство изменено не будет. После этого изменения надо закомитить.
В текстовом редакторе свойство svn:externals выглядит как одна или более строк содержащих имя директории для проекта от которого зависит проект и путь к этому проекту (можно указывать разные папки и ревизии). В нашем случае свойство выглядит следующим образом:
lib1 https://server/lib1/tags/1.0 lib2 https://server/lib2/trunk |
Возможно, что Subversion не вызовет текстовой редактор, а выдаст ошибку в которой сообщит, что не определена переменная окружения SVN_EDITOR. Это значит, что надо добавить в системное окружение данную переменную, указав путь к текстовому редактору, который будет вызван. Например C:/WINDOWS/system32/notepad.exe для Windows (обратите внимание — слеши надо разворачивать) или /usr/bin/vi для Linux.
Удалить свойство можно командой
svn propdel svn:externals |
И все?
Похоже на то. Теперь каждый раз забирая код project из репозитория будет одновременно подгружаться код библиотек. Если в библиотеках были сделаны изменения, то они автоматически обновятся. Таким образом прописав зависимости проекта можно не думать о библиотеках, они будут обновлены и скачаны автоматически, основная ветка разработки будет содержать папки lib1 и lib2 с библиотеками.
Следующий закономерный этап — автоматически собирать необходимые библиотеки вместе с проектом, о чем я скоро расскажу на примере кроссплатформенного инструмента автоматизации сборки CMake.
Информация, актуальная на любой момент времени:
ветки trunk, … придумали люди для наглядности и они вовсе не обязательны для получения нужных версий ПО, тем более посредством сервера CI. Есть номер ревизии и комментарий — этого достаточно ( так, можно разбирать комментарии на предмет выявления оговоренных соглашений, если очень нужно ).
Информация, актуальная на данный момент времени:
Вместо branch лучше использовать Git и локальные репозитории.
Iakov Minochkin
30 марта 11 19:42