Создавая хтонических чудовищ, документируй
Под данным изречением, взятым с замечательной картинки-мема неизвестного (по крайней мере, мне) автора, поставит свою подпись каждый человек, имеющий хотя бы отдалённое отношение к программированию. Весь вопрос, как? Как именно документировать-то?
Нижележащий текст преследует несколько целей:
- Во-первых, дать краткий обзор (читай — немного погундеть на тему) неудовлетворительного состояния инструментария, применимого к хтоническим чудовищам мира C/C++;
- Во-вторых, предложить своё альтернативное решение (бесплатно-без-СМС-и-регистрации — проект некоммерческий и выложен на GitHub под MIT-лицензией);
- В-третьих, призвать сообщество пообщаться на тему и собрать идеи;
- В-четвёртых, пригласить присоединиться к разработке проекта на 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 — например, упечь объявления в и расставить отступы и пробелы в соответствии с принятым в данной конкретной библиотеке 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 в применении к различным языкам:
- Jancy Standard Library Reference (язык Jancy)
- Jancy C API Reference (язык C)
- IO Ninja API Reference (язык Jancy)
- AXL Library Reference (язык C++)
Несмотря на незаконченность содержательной части документации по ссылкам выше (собственно описания классов, функций и т.д.), всего этого должно быть достаточно для демонстрации работоспособности метода.
Страничка проекта на GitHub: http://github.com/vovkos/doxyrest
Проект выложен под одной из самых нестрогих лицензий в мире — The MIT License. Смотрите, пробуйте, присоединяйтесь к разработке. А я с удовольствием отвечу на все вопросы в комментариях.
Комментарии (1)
27 декабря 2016 в 15:45
+3↑
↓
Эта вот манера постоянно вставлять жирный шрифт довольно-таки сильно затрудняет чтение этого текста.