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

Предыстория

При работе над документацией к ЗОСРВ «Нейтрино» и другим нашим продуктам требовалось спроектировать такое решение, которое было бы достаточно гибким для того, чтобы документировать разные сущности (драйверы, библиотеки, утилиты, функции API) и при этом сохраняло максимальную лаконичность и простоту для разработчиков. Код ЗОСРВ «Нейтрино» преимущественно написан на языке С, поэтому нам был необходим парсер исходного кода. Требовалось размещать разметку документации не только в файлах исходного кода, но и в standalone-файлах. Вдобавок, у нас имелся багаж из некоторого количества документации с разметкой Doxygen, которую нельзя было потерять, и еще лучше – не переделывать. Поэтому за основу был взят старый добрый Doxygen, поскольку он максимально прост и нетребователен, и при этом предоставляет достаточную функциональность для нашего видения того, как должна в итоге выглядеть на экране типовая страничка, как она должна быть оформлена. Были опробованы еще несколько решений, например, Sphinx и AsciiDoc. Первый не умеет парсить код на С, однако в нём есть возможность использовать в конечном итоге тот же Doxygen для парсинга, а со вторым просто не сложилось.

Исторически, наша справка использует Eclipse в качестве средства доставки контента до читателя. Данный сервер не является обязательным фактором, однако пока имеется внутренний набор legacy-документов, отказываться от которых мы не спешим. Сервер заворачивает разделы документации в JAR-архивы. Особенности формирования JAR-архивов заключаются в наличии специальных служебных файлов, в частности, файла с содержанием (иерархии). Здесь уже стандартные механизмы Doxygen трудноприменимы, и требовались серьёзные доработки, выродившиеся в отдельную систему автоматической генерации справочных материалов, которая использует Doxygen не более чем как парсер и генератор отдельных конечных страниц выходного формата.

И наконец поднялся вопрос об интеграции этой системы в процесс разработки. У команд разработчиков был выработан рабочий процесс (workflow), позволивший без особых проблем интегрировать систему генерации справки в разработку своих продуктов. При разработке и сопровождении продукта алгоритм действий разработчика выглядит следующим образом:

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

Рабочий процесс

Чтобы сопровождать наши программные продукты, система генерации справки должна была решать следующие поставленные задачи:

Для реализации поставленных задач было решено использовать связку из нескольких технологических решений.

Из репозиториев проекта берутся необходимые файлы, которые ведутся разработчиками и которые содержат в себе специальную разметку – разметку в синтаксисе Doxygen с расширениями уже нашей разработки. Содержимое этих файлов будет конвертироваться в выходной формат (это может быть HTML, либо PDF) при помощи нашей системы генерации справки на основе Doxygen. Если говорить исключительно о генерации страниц документации, то упрощённо она представляет собой препроцессирование файлов, вызов Doxygen, затем постпроцессирование уже выходных файлов. Запуск такой конвертации, сборка ее результатов и развертывание получившегося архива с готовой справкой на серверах выполняется при помощи системы непрерывной интеграции CI/CD. При этом у разработчиков имеется простой веб-интерфейс в системе CI/CD, что позволяет им не вникать в механизмы генерации справки.

Некоторые преимущества, которые мы получили

Контроль покрытия документацией

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

Получение сведений о текущем состоянии API (количество сущностей) выполняется в динамике с использованием LLVM/clang для анализа исходников библиотек.

Динамическое связывание страниц

Давайте рассмотрим некоторые расширения стандартного синтаксиса Doxygen. На изображении ниже можно увидеть локальное внутристраничное оглавление, и первый раздел из этого локального оглавления (“Блочные устройства”). Этот раздел содержит в себе две таблицы. Обратите внимание, что функции находятся в первой таблице, а пользовательские типы - во второй. Такое разбиение выполняется автоматически как раз при помощи механизма динамического связывания. Помимо этого, описания сущностей, которые вы видите в правом столбце таблицы подхватываются автоматически из тех страниц, на которые ведет соответствующая ссылка в левом столбце. При изменении описания на странице куда ведет ссылка, новое описание мы увидим и здесь.

Этот же механизм динамического связывания используется и для ведения журнала изменений продукта от версии к версии. На изображении ниже по левую сторону можно увидеть журнал изменений для ЗОСРВ «Нейтрино» редакции 2021 относительно редакции 2020. Внутри этого журнала есть разбиение по компонентам, например, драйверам и библиотекам. Справа же располагается конкретный список изменений по каждому компоненту внутри группы компонентов “Сервисы и менеджеры”. Гораздо удобнее просматривать новости релиза в одном месте, а не собирать их по всей справке. Журнал изменений фиксирует добавление, изменение или удаление компонентов, или отдельных их составляющих (опций у утилит, параметров у функций), а также любые другие изменений, которые внесли разработчики вручную, чтобы особенно подчеркнуть и обратить внимание пользователей.

Процесс формирования Release Notes состоит из нескольких шагов. Во-первых, во многих репозиториях с исходниками размещается шаблон будущего Release Notes, который детализирует локальные изменения по-релизно и по-компонентно (включая внутренние заметки, которые будут вырезаны при публикации Release Notes). Данные шаблоны сливаются в один большой шаблон, с сохранением релизно-компонентного ранжирования. После этого со всех страниц документации собираются сведения об указанных разработчиками тэгах @since и @deprecated и добавляются в общий шаблон. Это позволяет избегать необходимости дублировать информацию на странице какой-нибудь утилиты и в Release Notes, при изменении её опций и т. п.

Динамическое связывание также предоставляет программистам специальные элементы, которые упрощают работу по составлению разметки. К примеру, упрощенные ссылки, состоящие исключительно из наименования страницы, на которую надо сослаться. Адрес ссылки подставится автоматически при генерации.

Генерация справки по зафиксированным версиям продукта

Ниже приведен скриншот веб-интерфейса системы непрерывной интеграции, которая позволяет разработчику выбрать под какую версию операционной системы генерировать справку, ниже можно выбрать - хотим ли мы получить на выходе документацию для внутреннего использования или же публичную. И ниже можно выбрать, на какой сервер мы можем разместить архив с получившейся документацией - на тестовый, на внутренний или на внешний официальный. При публикации на внешние сервера добавлены дополнительные проверки утечки внутренних сведений (@internal). Интерфейс достаточно простой и не нагружает разработчика подробностями генерации справки.

Блоки информации для внутреннего использования

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

Как мы документируем программные продукты

Рассмотрим более детально процесс документирования программного продукта при помощи инструмента генерации справки.

Документирование в файлах исходного кода

Предположим, что мы написали функцию сложения двух чисел с именем add(). Давайте рассмотрим процесс ее документирования. Находим в заголовочном файле прототип функции, и размечаем многострочный комментарий.

Для начала зададим имя выходного файла, которое нам требуется, и заголовок, который мы хотим видеть на странице и предваряем эту информацию тегом @page. Теперь добавим краткое описание функции при помощи тега @brief, буквально в одном предложении. Далее, выделим важную информацию с помощью блока подсказки @hint и отметим, что эта функция появилась в проекте, начиная с версии 2.0.

Потом мы можем создать таблицу, например, с входными параметрами. Отдельно стоит отметить, что Doxygen по умолчанию не позволяет создавать вложенные таблицы, в то время как наша система этот недостаток устраняет и позволяет вкладывать друг в друга неограниченное количество элементов. Причем не только таблиц, но и к примеру, блоки подсказок тоже можно вкладывать друг в друга.

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

Готово, мы задокументировали нашу функцию!

Документирование отдельно от исходного кода

Только что мы рассмотрели один из вариантов написания разметки справки - в файле исходного кода проекта. Допустим, тот комментарий, что мы только что написали, находится вместе с функцией add() в файле header.h. При применении системы автоматической генерации у нас на выходе получится страница add.html, которая будет содержать информацию о функции add(). На каждую сущность в заголовочном файле (другие функции, классы, типы), для которой требуется страница справки, необходимо разметить аналогичный собственный комментарий.

Однако, бывает необходимость писать справку не только для мелких составляющих проекта вроде функций в API, но и для более крупных, например, для целой утилиты. В таком случае, подход с размещением комментариев в исходном коде не слишком очевиден и куда удобнее разместить справку как отдельный файл в директории этой самой утилиты. Например, файл utility.dox, который находится в какой-то папке внутри проекта. Содержимое этого файла аналогично тому, что мы видели при документировании в исходном коде, за исключением того, что он не оформляется как многострочный С-комментарий. На выходе для одного dox-файла мы получаем одну HTML-страницу.

Что дальше?

Однако, получения единичных изолированных справочных страниц недостаточно. Необходимо иметь дерево, по которому можно перемещаться по разным страницам. Так, функцию add() можно поместить в страницу API, а утилиту utility в страницу со списком утилит. Подразумевается, что одна страница является главной по отношению к нескольким другим, она содержит некую общую информацию и содержит ссылки на страницы с более узкоспециализированной информацией. В свою очередь, страницы с API и с утилитами можно объединить в какую-то основную, головную страницу для всего проекта. Таким образом можно получить дерево, по которому можно перемещаться внутри справки. Динамическое связывание выполняет эту задачу.

Как ознакомиться с результатами

Продемонстрировать результаты работы системы генерации в рамках статьи нереально, поскольку страниц очень большое количество, поэтому предлагаем вам ознакомиться с результатами более детально, перейдя на сайт нашей организации - ООО "СВД ВС". Также можно перейти сюда с домашней страницы сайта kpda.ru, перейдя в раздел "Документация", располагающийся наверху.

Одним из перспективных направлений развития нашей системы является генерация документации в формате PDF. То есть аггрегирование отдельных статей, страниц в один большой структурированный документ с титульным листом и содержанием, который можно листать как книгу. Наличие такого документа избавляет нас от необходимости поддержки множества серверов, плюс к этому документ можно распечатать и поставлять заказчикам. Генерация PDF (через LaTeX) интересна как с точки зрения фиксирования информации по определенной версии продукта, так и с точки зрения стандартизации и автоматизации оформления справочных материалов.

В данный момент PDF-документ по ЗОСРВ «Нейтрино» не размещён на нашем официальном сайте, однако, он будет публиковаться, начиная со следующего релиза ЗОСРВ «Нейтрино» в порядке эксперимента, так как там еще есть чем позаниматься в части структуры и стилей. Документы в формате PDF уже в полной мере поставляются заказчикам по некоторым программным продуктам, например, по ПК ЦКИ для ЗОСРВ «Нейтрино».

Спасибо за уделённое время и внимание!

Комментарии (4)


  1. silverpopov
    24.12.2022 13:33

    Эта ОС имеет какое-то отношение к QNX Neutrino, или это просто совпадение?


    1. s_zyl
      25.12.2022 11:42

      Вопрос замечательный :) Почему - чуть ниже, после ответа :)

      Краткий ответ: и "да", и "нет")

      Длинный ответ: эскизный проект по созданию этой ЗОСРВ был запущен ещё в 2005 году, пригодная для коммерческого применения версия вышла в 2011-ом. Сначала она была одним из нескольких независимых форков QNX (в мире как минимум полдюжины таких форков известны по открытым источникам), но со временем она стала так сильно отличаться от прототипа, и с каждым релизом их всё сложнее сравнивать. Сейчас это никак не связанные проекты, ОС работают на разных процессорных архитектурах, имеют разные графические подсистемы, разработка для них ведётся с помощью разных инструментальных средств...

      Теперь о том, в чём замечательность Вашего вопроса :) Дело в том, что мы с коллегами оживлённо дискутировали, нужны ли в принципе статьи про ОС. Я, честно говоря, придерживался той точки зрения, что писать про ОС и её историю скучно - наши пользователи прекрасно знают и нас, и наши разработки. Мои оппоненты настаивали, что сначала нужна статья именно про операционную систему, иначе первый же вопрос будет "что это за ОС?" Признаю, что они были правы, хорошо что мы не стали ставки делать)))


      1. silverpopov
        26.12.2022 12:22

        "Сначала она была одним из нескольких независимых форков QNX (в мире как минимум полдюжины таких форков известны по открытым источникам) "

        Что-то я сомневаюсь, что их лицензия позволяет это делать.

        "Сейчас это никак не связанные проекты "

        Ну да, ну да... )))

        "разные графические подсистемы"

        Которые почему-то называются одинаково, Photon.


        1. a-n-d
          26.12.2022 13:39

          Что-то я сомневаюсь, что их лицензия позволяет это делать

          Лицензия для потребителей не позволяет. Объем приобретенных нами прав - да.

          Ну да, ну да... )))

          Открываем changelog на релизы у нас и у них и сравниваем, более веские аргументы найти будет сложно. Выше на него ссылка дана (включен в документацию). Наши рабочие процессы на этот уровень не сразу вышли, но с релиза 2018 более-менее прокачались.

          Которые почему-то называются одинаково, Photon

          То, что вы назвали - не графическая подсистема, а ее клиент, реализующий оконные функции (очень отдаленная аналогия в Linux - Kernel+DRM и клиент в виде пары X11+KDE). Его, к слову, в QNX давно уже нет.

          Мы же, из соображений поддержки обширного пласта его пользователей, сам Photon не удаляем из дистрибутива. Слишком много проектов с ним вертится и продолжает разрабатываться.

          На тему развития скоро опубликуем отдельную статью. Она сейчас опубликована в свежем номере журнала "Системы управления и обработки информации" (№58(3)). На праздничных выходных должно найтись время для ее переработки с прицелом на уменьшение степени официальности.