Опубликован: 06.10.2011 | Доступ: свободный | Студентов: 1677 / 94 | Оценка: 4.67 / 3.67 | Длительность: 18:18:00
Лекция 5:

Инструментарий

< Лекция 4 || Лекция 5: 123 || Лекция 6 >

4.2. Управление конфигурацией

Программная система имеет тенденцию изменяться с течением времени. Сложное ПО состоит из многих частей и разрабатывается многими людьми.

Если рассматривать эти три характеристики совместно, то можно увидеть серьезные проблемы:

Три измерения управления конфигурацией ПО

Рис. 4.2. Три измерения управления конфигурацией ПО

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

Разнообразие в управлении конфигурацией

Источником, который управлению конфигурацией и дал это имя, является задача по комбинированию или конфигурированию отдельных частей ПО в единую систему. Для простоты рассмотрим два измерения – "Части" и "Изменения". В случае программ частями являются модули, такие как методы, классы и кластеры. Каждый модуль проходит через последовательные версии, в соответствии со своим собственным расписанием и ограничениями; по аналогии с музыкальным произведением, это скорее контрапункт, чем гармония, скорее фуга Баха, чем военный марш. Но системе в целом нужно развиваться в своем ритме, имея собственную историю. Приходится отдельные куски склеивать вместе в их текущем состоянии и выпускать очередной релиз системы. На жаргоне скомпонованная система называется "билд", или сборка (build), и это источник, угрожающий бедствиями.

Так просто использовать версию 3.1 модуля А и версию 2.5 модуля В, в то время как версия модуля А прошла сертификацию на совместную работу с версией 2.4 модуля В. Многие катастрофические ошибки в работе ПО возникали по вине этой, казалось бы, тривиальной ошибки управления сборкой. Ошибки перестают быть тривиальными с ростом размера системы и времени ее существования.

Но управление конфигурацией не сводится только к проблемам сборки. Сложности могут возникать даже при разработке единственного модуля. Вот типичные вопросы:

  • Когда модуль был последний раз модифицирован?
  • Кто модифицировал модуль в период между сентябрем и декабрем прошлого года?
  • Когда была выпущена версия n?
  • Что изменилось в версии n +1 в сравнении с версией n?
  • Что послужило причиной данного изменения?
  • Эта ошибка все еще существует в текущей версии?
  • Если нет, то когда она была исправлена?
  • Можем ли мы вернуться к версии модуля М от 15 марта этого года?

Данный аспект управления конфигурацией называют контролем версий.

Большинство широко распространенных средств управления конфигурацией занимаются именно этими двумя вопросами – автоматической сборкой и контролем версий. Мы рассмотрим оба эти аспекта, а в следующем разделе обсудим проект глобального хранилища (репозитория), расширяющий управление конфигурацией до поддержки общей инфраструктуры проекта.

Инструменты сборки: от Make до автоматического анализа зависимостей

Источником вдохновения для построения инструментария сборки послужила команда Unix – Make, разработанная Стюартом Фельдманом в 1977 году.

Реконструкция системы по заданному описанию зависимостей между модулями основывается на использовании файла сборки – makefile, представляющего список входов в форме:

target: source1, source2 …
    command1
…
        
Стюарт Фельдман (2006)

Рис. 4.3. Стюарт Фельдман (2006)

Это описание говорит, что цель target включает источники source, которые обычно задаются файлами. Всякий раз, когда один из источников изменяется, цель должна быть перестроена, для получения цели из источников нужно выполнить команду или группу команд command, указанную в файле сборки.

Выполнение

make target
        

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

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

Типичный файл сборки для построения программы, написанной на С, имеет вид:

program: main.o module1.o module2.o
    cc main.o module1.o module2.o
%.c: %.o
    cc $<
        

Для программ на С принято сохранять их в файлах с расширением .c, например name.c. Компилятор – команда cc – генерирует по исходному коду объектный код, записывая его в файл с расширением .o (name.o). Команда cc выполняет двойную работу: будучи линкером, она применяется к одному или нескольким объектным файлам, создавая новый модуль с удаленными перекрестными ссылками. В приведенном файле сборки указано, что наша программа program должна быть собрана из трех объектных модулей (main – это главный программный модуль); для генерирования программы нужно применить к ним компилятор cc. Для описания того, как из исходного модуля получать объектный, можно было бы использовать три зависимости в форме

main.o: main.c
    cc main.c
        

Аналогично можно было бы записать зависимости для module1 и module2. В файле сборки все три зависимости объединены в одно общее правило, применимое к любому -файлу; символ % является держателем места – вместо него может быть подставлено любое имя. Команда в строчке $< означает, что она должна быть применена к результату, полученному в предыдущей строке файла сборки (конечно, нотация не слишком прозрачная, но это простительно, особенно если учесть, что с 1977 года Make и его потомки помогли миллионам программистам правильно конфигурировать свои программы).

Команда make program будет делать то, что и ожидалось: вначале скомпилируются три исходных модуля и будут созданы соответствующие -файлы; затем они будут скомпонованы (снова той же командой cc), создав в результате program. Заметьте, Make автоматически определяет порядок этих операций, анализируя зависимости файла сборки.

Эти концепции применимы не только к программированию. Например, файл сборки можно было бы применить для генерации документации, где зависимости включали бы исходные файлы для документов в разных форматах (Microsoft Word, Open Office, TeX, Frame Maker), а команды позволяли бы преобразовывать эти документы в единый формат HTML или PDF.

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

Более современный подход состоит в том, чтобы поставлять документируемую программу, содержащую всю необходимую информацию для автоматического построения зависимостей. Это то, что делает, в частности, EiffelStudio, – здесь нет аналога файла сборки, поскольку компилятор сам может определить, когда метод или класс был изменен, какие классы от него зависят и, следовательно, должны быть перекомпилированы.

Контроль версий

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

Три измерения управления конфигурацией ПО

Рис. 4.4. Три измерения управления конфигурацией ПО

Части (модули в случае программ) подвергаются последовательным изменениям. Мы не можем позволить разработчикам редактировать их по своему усмотрению, а затем перекомпилировать всю систему. Это привело бы к хаосу. Вместо этого каждое изменение должно быть записано – кто, когда, что и почему. Необходимо уметь сравнивать последовательные версии и уметь откатываться к предыдущей версии.

Существует достаточно много средств контроля версий, коммерческих и свободно распространяемых. Некоторые из них – достаточно сложные интегрированные системы, хотя наиболее успешные являются простыми средствами, сфокусированными на основных проблемах, которые позволяют интегрировать их без особых усилий в процесс разработки ПО. Наибольшую известность получила линейка инструментов, название первого из которых было четырехбуквенным акронимом, а остальные стали трехбуквенными. Марк Рочкинд из Bell Labs разработал в 1972 году инструмент SCCS (Source Code Control System). Он был усовершенствован Уолтером Тичи в 1982 году под названием RCS (R – от Revision).

Уолтер Тичи (2006)

Рис. 4.5. Уолтер Тичи (2006)

Позже появился CVS (1986, C – от Concurrent, основанный на RCS), последняя версия – это SVN, новая реализация CVS.

Установка такой системы включает:

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

Репозиторий хранится на сервере, и пользователи обычно получают доступ через сеть. Две фундаментальные операции доступны для пользователей:

  • Обновить (Update) – создать локальную копию части, хранящейся в репозитории. По умолчанию выдается последняя версия части, но можно получить и любую предыдущую версию;
  • Зафиксировать (Commit) – ввести часть в репозиторий, возможно, новую, но чаще модифицированную версию существующей части.

Фиксация создает новую версию, снабжая ее идентификатором версии. Общепринято идентификаторы версий задавать в виде последовательности чисел, разделяемых точкой.

Входной и выходной контроль

Рис. 4.6. Входной и выходной контроль

Так, на момент написания этого текста версия EiffelStudio имела номер 6.3, где 6 – это главный номер, который изменяется только для версий, вносящих принципиальные изменения, 3 – это младший номер, изменяющийся при каждом новом выпуске системы. Промежуточные версии, которые вносят, например, заплатки (patch), устраняющие ошибки, будут нумероваться как 6.3.m или даже 6.3.m.n.

Концептуально репозиторий сохраняет все версии. Кажется, что эта цель недостижима, поскольку требует чрезмерно большой памяти, Реалистичной эту технологию делает введение понятия "diff" – различие, название, пришедшее от команды Unix, которая для двух переданных ей файлов показывает их различие – строки добавлены, строки удалены, строки изменились. Если вы когда-либо, будучи на странице History в Википедии, выбирали "Compare selected versions" ("Сравнить выбранные версии"), то могли видеть подобную картину сличения различий:

Представление "diffs" двух версий файла, скажем, file_n и file_{n-1} предназначено для человеческого восприятия, но diff-алгоритм может также вырабатывать специальную форму d, которая позволяет сопутствующему алгоритму реконструировать file_n, зная d и file_{n-1}, или получить file_{n-1} по file_n и d.

Сопутствующий алгоритм прямолинеен: d описывает последовательность строк, добавляемых, удаляемых, изменяемых, поэтому достаточно применять эти операции к файлу в заданном порядке. Алгоритм diff не столь прост.

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

Как показано на примере Википедии, контроль версий полезен не только для программных модулей. Например, в нашей группе в ETH многие преподаватели работают над слайдами больших курсов, – все слайды хранятся в репозитории.

В случае разработки ПО контроль версий применяется не только к программным модулям, но и ко всем другим документам программного проекта, начиная от документа требований, продолжая документами проектирования и заканчивая результатами тестирования. Контроль версий прост в использовании и предотвращает многие неприятности. Следующее правило является одним из наиболее важных правил, которому необходимо следовать в процессе разработки ПО.

Почувствуй методологию
Используй контроль версий

Сохраняйте весь код и все документы программного проекта под управлением системы контроля версий. При фиксации версии всегда записывайте причину и природу изменений.

Вторая часть совета связана с возможностью при фиксации новой версии ввести сообщение, которое будет храниться вместе с изменениями. Можно этого не делать, но так поступать не следует. Ситуация аналогична заданию заголовочного комментария для метода. Систематическое применение этого правила приводит к тому, что со временем репозиторий превращается в обогащенную базу знаний, хранящую эволюцию ПО.

Рассмотренный сценарий контроля версий прекрасно работает для случая единственного разработчика модуля. Более сложная ситуация возникает, когда несколько человек работают над одним и тем же модулем, например, классом. Конечно, на время обновления каждый разработчик может закрывать модуль для других пользователей, но обычно это слишком строгое требование. Когда вы фиксируете свои изменения после фиксации изменений другого разработчика, система контроля версий обнаружит конфликт и попросит вас разрешить его, предоставляя вам в помощь файл различий. В большинстве случаев конфликт легко разрешается, поскольку разные разработчики работают над разными классами или разными методами одного класса, но если возникает реальный конфликт, когда изменения пересекаются, то его разрешение требует внимательного рассмотрения. Не следует накапливать изменения, поскольку это может приводить к сложностям разрешения конфликтов.

Почувствуй методологию
Чаще фиксируйте изменения

Каждое важное изменение следует фиксировать. Это позволяет минимизировать конфликты и облегчает их разрешение.

Дополняющая часть совета включает "ветвление".

Почувствуй методологию
Ветвление

Не создавайте новую ветвь контроля версий, если только не собираетесь на старой основе разработать новый, независимо сопровождаемый продукт.

В системе контроля версий доступно ветвление, позволяющее разделить продукт на два, каждый со своей собственной системой идентификации. После ветвления каждая ветвь начинает жить собственной жизнью, так что через некоторое время их объединение становится трудной задачей.

Соблазн ветвления возникает достаточно часто, когда несколько разработчиков, работая над одним и тем же кодом, развивают его в разных направлениях, – понятно их желание работать независимо, оставляя проблемы воссоединения на будущее. Мудрость, накопленная сообществом разработчиков за многие годы, говорит, что подобная независимость – плохая идея. Небольшие хлопоты по разрешению небольших ежедневных конфликтов предпочтительнее "большого взрыва", когда собираются все разработчики, предлагающие результаты работы за несколько месяцев упорного труда, и у каждого из них все работает хорошо, но отказывается работать в совместном варианте.

Единственный случай, когда допускается ветвление, – это создание новой независимой линейки программного продукта.

Например, EiffelStudio имеет исследовательскую версию EVE (Eiffel Verification Environment), которая является ветвью версии 6.2. В данном случае обе ветви все еще остаются синхронизированными, подвергаясь регулярным реконструкциям, поскольку изменения имеют тенденцию воздействовать на разные части системы.

Управление конфигурацией, включающее управление сборкой, контроль версий, так же как и более продвинутые приложения, – это те "лучшие практики", отработанные современной инженерией программ, и их следует применять в каждом проекте – большом или малом.

< Лекция 4 || Лекция 5: 123 || Лекция 6 >