Создавая хтонических чудовищ, документируй

в 12:13, , рубрики: api, C, c++, Lua, документация, Программирование

ioninja

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

Нижележащий текст преследует несколько целей:

  1. Дать краткий обзор (читай — немного погундеть на тему) неудовлетворительного состояния инструментария, применимого к хтоническим чудовищам мира C/C++;
  2. Предложить своё альтернативное решение (бесплатно-без-СМС-и-регистрации — проект некоммерческий и выложен на GitHub под MIT-лицензией);
  3. Призвать сообщество пообщаться на тему и собрать идеи;
  4. Пригласить присоединиться к разработке проекта на GitHub.

Сразу оговорюсь, что хотя проект создавался в первую очередь как альтернатива, а точнее, дополнение Doxygen для сишных и плюсовых API, архитектурно он в равной степени пригоден и для других языков. Это позволяет создавать порталы документации разноплановых библиотек — сами библиотеки могут быть написаны на разных языках, а в документации будет единство стиля во внешнем виде и поведении.

Мотивация

По большому счёту, подходов к документированию API и библиотек — плюсовых или нет — ровно два.

  • Первый, это писать всё ручками.

Даже неважно в чём — в Help & Manual, RoboHelp, Word или другом редакторе. Несмотря на то, что этот традиционный способ всем понятен и по-прежнему широко используется, я глубоко убеждён что он в корне неверен. Дело в том, что он порождает документацию, которая всё время местами нерелевантна и отстаёт от объекта документации. Поддержка согласованности между созданными раздельно, а зачастую ещё и разными людьми, документацией и постоянно эволюционирующим API библиотеки — не эволюционируют только умершие или замороженные продукты! — это колоссальная задача, лишь немногим более лёгкая написания первичной документации.

  • Второй, "правильный" подход, состоит в том, чтобы генерировать документацию по исходникам автоматически.

Специально обученный парсер пробегает по исходникам, вычленяет особым образом оформленные комментарии с документацией и строит структуру дерева публичных объявлений API. После этого генерируется документация в нужном формате — меня, как, полагаю, и большинство, в первую очередь интересует HTML и PDF. Основным преимуществом данного подхода является гарантированная когерентность объявлений в исходниках API и в конечной документации. Даже при полном отсутствии в исходниках содержательных комментариев с собственно "документацией", в конце мы будем иметь прекрасный снимок состояния API библиотеки, с возможностью "попрыгать" по объявлениям и описаниям типов, и т.д.

Итак, с вашего позволения, я сконцентрируюсь на "правильном" подходе с автогенерацией. Какие варианты у нас имеются тут? Увы, для документации C/C++ на данный момент имеется и реально используется печально мало: Doxygen да QDoc. И с этими двумя тоже далеко не всё гладко.

Doxygen — первый по-настоящему успешный проект по вытаскиванию комментариев из кода на плюсах и превращению оных в HTML-документацию с гиперссылками, картинками графов наследования, вызовов и т.д. В отличие от своего прямого родителя — первопроходца Doc++, так никогда и не получившего достаточного распространения, Doxygen сейчас — это де-факто стандарт документирования кода на C/C++. И всё это было бы замечательно, если бы не два "но":

  • Стандартный генерируемый доксигеном HTML, как бы это помягче сказать… не обременён элегантностью.

Конечно, тут есть место субъективизму. Я вполне допускаю, что в мире существуют не столь придирчивые люди, которых доксигеновский выхлоп полностью устраивает (рискну предположить, однако, что профессиональных дизайнеров среди них не окажется). Но даже если умолчальный доксигеновский HTML и устраивает кого-то с визуальной точки зрения (а серьёзно, есть такие, кому он и вправду нравится эстетически? напишите в комментариях!), очень часто хочется поменять и настроить что-то выходящее за рамки подкручивания CSS — например, упечь объявления в <pre> и расставить отступы и пробелы в соответствии с принятым в данной конкретной библиотеке coding-style. Это подводит нас ко второй, более фундаментальной проблеме Doxygen:

  • Doxygen за свою долгую жизнь так и не отрастил настоящую, модульную настраиваемость.

Да, есть Doxyfile с кучей переменных, есть возможность менять HTML шапки и CSS, но архитектурно — всё захардкожено в монолитное C++ ядро! Причём захардкожен как front-end, а именно, парсеры исходников, так и back-end — генераторы HTML, PDF, RTF и другого (среди которого, слава небесам, есть и XML).

QDoc по умолчанию выдаёт гораздо, гораздо более симпатичный HTML, чем Doxygen. К сожалению, если требуется что-то не по умолчанию, то QDoc страдает всё той же врождённой деревянностью, что и Doxygen (растущей, понятно, из той же самой ж... захардкоженности и парсера, и генератора в монолитное плюсовое ядро). В дополнение к своей деревянности, QDoc, в отличие от Doxygen, имеет всего лишь один входной парсер — для QT-диалекта C++ (со всеми Q_OBJECT, Q_PROPERTY, foreach, и т.д. жёстко трактуемыми как ключевые слова). И при этом, — что уж совсем ни в какие ворота, — не умеет генерировать PDF!

Альтернатива

Предлагается заменить один инструмент конвейером. Вместо

Doxygen -> (HTML, PDF, ...)

… будем использовать следующий pipeline:

Doxygen -> (XML) ->
    -> Некий-Мост -> (reStructuredText) ->
        -> Sphinx -> (HTML, PDF, ...)

Что оставляем старого?

Разработчики знают как и уже привыкли документировать C/C++ код с помощью Doxygen-комментариев:

/*!
    brief This is a brief documentation for class Foo.

    This is a detailed documentation for class Foo.
    ...
 */
class Foo
{
    // ...
}

Зачем изобретать новый синтаксис? Будем писать документацию так же, как и раньше!

Doxygen умеет вытаскивать документацию из исходников и класть её вместе с деревом объявлений в XML базу данных. Прекрасно! Это будет нашим front-end.

Ещё легче ответить на вопрос о том, что использовать в качестве back-end — конечно, Sphinx. Sphinx заслуженно получил колоссальное распространение как инструмент написания технической документации. Он выдаёт весьма вкусно выглядящий HTML с поддержкой полноценных тем (а не просто CSS!), умеет склеивать всё в одну HTML-простыню, генерировать документацию в PDF, EPUB и множестве других форматов — и всё это из коробки! Но что самое главное, он полностью настраиваем с помощью Python-скриптов, причём их можно применять как для тюнинга внешнего вида, так и для расширения входного языка (каковым для Sphinx является reStructuredText) — а именно, дописывать свои директивы и потом использовать их в документации.

Осталось подружить Doxygen и Sphinx.

Строим мост

Замечу, что я не первый, кто пытался построить мост между Doxygen и Sphinx. Относительную известность приобрёл проект breathe, написанный на Python как расширение для Sphinx. В настоящий момент проект не слишком активно ковыряется отвёрточкой, и, увы, из коробки не пригоден для серьёзных задач. Архитектурно он устроен следующим образом: он парсит XML-выхлоп доксигена и создаёт узлы reStructuredText дерева в памяти напрямую.

Я же решил пойти несколько другим путём. Doxyrest — так называется наш мост — парсит доксигеновские .xml файлы, а затем отдаёт распарсенный XML и набор файлов-шаблонов в шаблонизатор (string template engine, template processor). Шаблонизатор генерирует файлы с reStructuredText, и уже эти .rst файлы передаются в Sphinx-back-end для получения окончательной документации в заданном формате.

Основная фишка — конечно же, использование шаблонизатора. Это позволяет полностью настраивать структуру документации: менять порядок и группировать документируемые объекты (классы/функции/свойства и т.д.), настраивать стиль объявлений (где и как использовать отступы, пробелы, переносы строк и т.д.), использовать логику произвольной сложности для включения или не включения данного конкретного объекта в документацию, и так далее — и всё это без перекомпиляции, просто правкой входных шаблонов!

Но главное — подход с шаблонизатором позволяет применять Doxyrest для абсолютного большинства любых других языков, и в частности, разнообразных DSL — для которых никто и никогда не будет делать специализированных систем документации. Doxygen не умеет парсить ваш язык? Взяли компилятор языка, добавили туда генерацию Doxygen-подобного XML по уже имеющемуся AST, затем исправили шаблоны выходных .rst файлов — чтобы объявления в документации были с нужным синтаксисом, — и всё! Ваш язык теперь можно документировать с помощью Doxygen-комментариев и получать на выходе красивую Sphinx-документацию.

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

Выглядят и работают шаблоны как-то так:

    Title
    =====

    %{
    if false then
    }
    This text will be excluded..
    %{
    end -- if

    for i = 1, 3 do
    }
    * List item $i
    %{
    end -- for
    }

На выходе будем иметь:

    Title
    =====

    * List item 1
    * List item 2
    * List item 3

Примеры использования

Лучше один раз увидеть, чем сто раз услышать. Посему, вместо заключения я решил просто привести ссылки на результат работы Doxyrest в применении к различным языкам:

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

Страничка проекта на GitHub: http://github.com/vovkos/doxyrest

Проект выложен под одной из самых нестрогих лицензий в мире — The MIT License. Смотрите, пробуйте, присоединяйтесь к разработке. А я с удовольствием отвечу на все вопросы в комментариях.

Автор: vovkos

Источник

Поделиться

* - обязательные к заполнению поля