«…Труд избавляет человека от трех великих зол: скуки, порока, нужды…»

Использование Doxygen для документирования кода

Материал из Wiki
Перейти к: навигация, поиск

Содержание

Использование Doxygen

Использовать Doxygen просто – для этого надо просто запустить программу указав ей путь к файлу с настройками. Файл с настройками представляет собой простой текстовой файл, который можно редактировать как в текстовом редакторе, так и с помощью специальных программ, например Doxygate.

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

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

Что нужно для работы с Doxygen

Структура папок в проекте

  • /doxygen
    • /docs – результат работы программы
      • index.html - документация
    • /doxyfile
    • /filter – скрипт для преобразования sv -> cpp
    • /html – html-шаблон страницы (сейчас не используется)
    • /img – изображения для вставки на страницы документации
      • download_img.sh - скрипт для скачивания картинок
    • /scripts
    • dofilter.bat – скрипт запуска фильтра для Windows (запускается doxygen`ом)
    • dofilter.sh – скрипт запуска фильтра для Unix(запускается doxygen`ом)
    • dotest.pl
    • Doxyfile.delta – Параметры касающиеся конкретного проекта
    • Doxyfile.output – входной файл с параметры для Doxygen (слияние .template и .delta)
    • Doxyfile_sv.template – параметры по умолчанию для Doxygen
    • Makefile скрипт для запуска
  • /sv
    • info.txt - файл с декларациями групп/категорий в документации

Назначение файлов

makefile

Скрипт для генерации html-отчета

make

Doxyfile.delta

Файл настроек параметров проекта

#FILTER_PATTERNS = *.sv=./dofilter.sh *.svh=./dofilter.sh
FILTER_PATTERNS = *.sv=dofilter.bat *.svh=dofilter.bat
STRIP_FROM_PATH = ../
PROJECT_NAME    = UniPro (Pinta)
PROJECT_NUMBER  = 1.0.0
HTML_OUTPUT     = docs
#HTML_FOOTER    = <PATH_DOXYSCR>/html/idv_dox_footer.html
INCLUDE_PATH    = <PATH_PRJ>/../sv <PATH_PRJ>/../sv/pa_sap <PATH_PRJ>/../sv/pa_sap/sequences <PATH_PRJ>/../sv/pa_lm_sap 
                  <PATH_PRJ>/../sv/pa_lm_sap/sequences <PATH_PRJ>/../sv/phy_sap <PATH_PRJ>/../sv/phy_sap/sequences 
                  <PATH_PRJ>/../sv/scoreboard <PATH_PRJ>/../sv/scoreboard/l15_model <PATH_PRJ>/../tb
                  <PATH_PRJ>/../test/test_lib <PATH_PRJ>/../test/vseq_lib
INPUT           = <PATH_PRJ>/../sv <PATH_PRJ>/../sv/pa_sap <PATH_PRJ>/../sv/pa_sap/sequences <PATH_PRJ>/../sv/pa_lm_sap
                  <PATH_PRJ>/../sv/pa_lm_sap/sequences <PATH_PRJ>/../sv/phy_sap <PATH_PRJ>/../sv/phy_sap/sequences
                  <PATH_PRJ>/../sv/scoreboard <PATH_PRJ>/../sv/scoreboard/l15_model <PATH_PRJ>/../tb
                  <PATH_PRJ>/../test/test_lib <PATH_PRJ>/../test/vseq_lib
IMAGE_PATH      = img
  • для добавления новых папок проекта их нужно включить в переменные INCLUDE_PATH и INPUT
  • STRIP_FROM_PATH - путь, который исключается при выводе пути к файлу в документации

Файл Doxyfile.output

  • LAYOUT_FILE = ./doxyfile/idv_doxylayout.xml
  • FILE_PATTERNS = *.svh *.sv *.cpp *.c *.h *.vhd *.v *.txt *.md
  • FILTER_PATTERNS = *.sv=./filter/idv_doxyfilter_sv.pl *.svh=./filter/idv_doxyfilter_sv.pl

Пример отчета

Формат комментариев

Doxygen поддерживает несколько стилей комментариев:

/** 
 * Комментарии, воспринимаемые программой Doxygen
 */
/// Комментарий, воспринимаемые программой Doxygen
/// продолжение комментария
int count ;  ///< Счетчик повторов
int count ;  /**< Счетчик повторов */

Примеры тегов

Служебные

@brief

Краткое описание комментируемой сущности.

Пример:

/// @brief Функция для поиска пользователя в базе данных

@details

Подробное описание элемента (функции, класса и пр.), то что будет показано, если перейти по ссылке "More..." ("Подробней") в документации.

Пример:

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

Комбинации @brief и @details

/*! \brief Brief description.
 *         Brief description continued.
 *
 *  \details detailed description starts here.
 */
/*! \brief Brief description.
 *         Brief description continued.
 *
 *  detailed description starts here.
 */
/// Brief description.
/** detailed description. */

@param

\param [(dir)] <parameter-name> { parameter description }

Параметры передаваемые в функции/классе и пр.

Пример:

/**
 * A pure virtual member.
 * @param c1 the first argument.
 * @param c2 the second argument.
 */
 virtual void testMeToo(char c1,char c2) = 0;
/*!
* Copies bytes from a source memory area to a destination memory area,
* where both areas may not overlap.
* @param[out] dest The memory area to copy to.
* @param[in] src The memory area to copy from.
* @param[in] n The number of bytes to copy
*/
void memcpy(void *dest, const void *src, size_t n);


@return

Возвращаемое функцией значение.

Пример:

@return Информация о пользователе

@note, @warning, @todo

@note
\todo { paragraph describing what is to be done }
\warning { warning message }

@file, @class, @fn, @def, @enum

\fn (function declaration)
\file [<name>]
\class <name> [<header-file>] [<header-name>]
\dir [<path fragment>]
\def <name>
\enum <name>
class Test
{
 public:
   enum TEnum { Val1, Val2 };

   /*! Another enum, with inline docs */
   enum AnotherEnum 
   { 
     V1, /*!< value 1 */
     V2  /*!< value 2 */
   };
};

Для форматирования

Разметка

  • списки
- Item 1
 
   More text for this item.
 
 - Item 2
   + nested list item.
   + another nested item.
 - Item 3
  • Item 1
    More text for this item.
  • Item 2
    • nested list item.
    • another nested item.
  • Item 3
  • Выделения
    • _single underscores_single underscores
    • __double underscores__single underscores
  • вставка кода
16:15, 3 ноября 2014 (MSK)16:15, 3 ноября 2014 (MSK)16:15, 3 ноября 2014 (MSK){.c}
int func(int a, int b) { return a*b; }
16:15, 3 ноября 2014 (MSK)16:15, 3 ноября 2014 (MSK)16:15, 3 ноября 2014 (MSK)
  • таблица
| Right | Center | Left  |
| ----: | :----: | :---- |
| 10    | 10     | 10    |
| 1000  | 1000   | 1000  |

Вставка изображений

  • Первый вариант
\image <format> <file> ["caption"] [<sizeindication>=<size>]

Inserts an image into the documentation.

 /*! Here is a snapshot of my new application:
  *  \image html application.jpg
  *  \image latex application.eps "My application" width=10cm
  */
  • Второй вариант
![Caption text](/path/to/img.jpg "Image title")
![Figure - UVM top env architecture] (UVM_TOP_Architecture.png "UVM top env architecture")
  • IMAGE_PATH = img


Разделы и подразделы

  • \section <section-name> (section title) - Creates a section with name <section-name>.
  • \subsection <subsection-name> (subsection title) - Creates a subsection with name <subsection-name>. The title of the subsection should be specified as the second argument of the \subsection command.
  • \subsubsection <subsubsection-name> (subsubsection title)
  • \paragraph <paragraph-name> (paragraph title)

Страницы и подстраницы

\mainpage A simple manual 
\page  intro Introduction
\subpage <name> ["(text)"]

Содержание

\tableofcontents

Creates a table of contents at the top of a page, listing all sections and subsections in the page.

\code [ '{'<word>'}']
\code{.py}
 class Python:
    pass
 \endcode

Диаграммы GraphViz

/** \mainpage
 * Class relations expressed via an inline dot graph:
 * \dot
 * digraph example {
 * node [shape=record, fontname=Helvetica, fontsize=10];
 * b [ label="class B" URL="\ref B"];
 * c [ label="class C" URL="\ref C"];
 * b -> c [ arrowhead="open", style="dashed" ];
 * }
 * \enddot
 * Note that the classes in the above graph are clickable
 * (in the HTML output).
 */
GraphVizExample.jpg

Диаграммы MSCGEN

/** 
@msc
  описание рисунка
@endmsc
*/
/** @msc
arcgradient = 8;
a [label="Client"],b [label="Server"];
a=>b [label="data1"];
a-xb [label="data2"];
a=>b [label="data3"];
a<=b [label="ack1, nack2"];
a=>b [label="data2", arcskip="1"];
|||;
a<=b [label="ack3"];
|||;
@endmsc
*/
MscgenExample.png

ref

\ref <name> ["(text)"]

Creates a reference to a named section, subsection, page or anchor.

Организация структуры

\ingroup (<groupname> [<groupname> <groupname>])
  • ingroup добавлет описания class, file или namespace в соответствующую группу(ы).
\defgroup <name> (group title)
  • defgroup Indicates that a comment block contains documentation for a group of classes, files or namespaces. This can be used to categorize classes, files or namespaces, and document those categories. You can also use groups as members of other groups, thus building a hierarchy of groups.

The <name> argument should be a single-word identifier.

\addtogroup <name> [(title)]
  • создаёт новую группу либо добавляет описание к уже существующей группе
/*! \addtogroup mygrp
   *  Additional documentation for group 'mygrp'
   *  @{
   */
 
  /*!
   *  A function
   */
  void func1()
  {
  }
 
  /*! Another function */
  void func2()
  {
  }
 
  /*! @} */

Примеры

* @defgroup sequences   Sequences
* @{
* @brief Sequence classes
*
*   @defgroup pa_sap_tx_seq  PA_SAP TX Sequences
*   @defgroup pa_sap_rx_seq  PA_SAP RX Sequences
*   @defgroup pa_lm_sap_tx_seq  PA_LM_SAP TX Sequences
*   ...
* @}
/// @file l15_pa_sap_tx_sequences.sv
/// @ingroup pa_sap_tx_seq
/**
 * @class l2_data_base_seq
 * @ingroup pa_sap_tx_seq
 * @brief Consist of all base task for generate all sequence variants for test cases
 *
 */
class l2_data_base_seq extends uvm_sequence #(pa_sap_tr);


Доступные группы

  • agents Agents
    • drivers Drivers
    • monitors Monitors
  • env Environments
  • scoreboard Scoreboards
  • tests Tests
  • sequences Sequences
    • pa_sap_tx_seq PA_SAP TX Sequences
    • pa_sap_rx_seq PA_SAP RX Sequences
    • pa_lm_sap_tx_seq PA_LM_SAP TX Sequences
    • pa_lm_sap_rx_seq PA_LM_SAP RX Sequences
    • phy_sap_tx_seq PHY_SAP TX Sequences
    • phy_sap_rx_seq PHY_SAP RX Sequences
    • phy_sap_tx_cntr_seq PHY_SAP TX Control Sequences
    • phy_sap_rx_cntr_seq PHY_SAP RX Control Sequences
    • vsequence Virtual sequences
  • transaction Transaction (Sequence item)




Комментирование в стиле Doxygen

Doxygen поддерживает несколько стилей комментариев. Я придерживаюсь следующего:

/** Комментарии */

Обратите внимание, что последовательность символов /** сообщает программе, что начинается комментарий предназначенный для нее. Начиная с этого места и до завершающих символов */ следуют комментарии.

Есть несколько директив, которые помогают Doxygen составить грамотную документацию. Вот основные:

@brief Краткое описание комментируемой сущности.

Пример:
@brief Функция для поиска пользователя в базе данных
@details Подробное описание сущности, то что будет показано, когда вы нажмете в документации рядом с коротким описанием ссылку "Подробней". На мой взгляд использоваться должно как можно реже, в силу того, что короткое описание должно точно отражать идею использования сущности, а использование ее не должно сопровождаться неожиданными предусловиями.

Пример:
@details Данная функция делает выборку из базы данных
по имени пользователя и возвращает структуру с информацией
о нем. Ожидается, что соединение с базой данных установлено
и пользователь существует
@param Параметры передаваемые функции.

Пример:
@param name Имя пользователя
@return Возвращаемое функцией значение.

Пример:
@return Информация о пользователе
@throw Исключения выбрасываемые функцией.

Пример:
@throw DatabaseError Если произошла ошибка при подключении
к базе данных

Пример комментариев для класса:

/** @brief Класс для работы с базой данных
 @details Осуществляет подключение к базе при создании  и закрывает соединение при уничтожении
 */
class Database
{
public:
/** @brief Конструктор
    @param connectionString Строка подключения к базе данных
    @throw ConnectionError Если подключение не удалось
 */
   explicit Database(const std::string& connectionString);
/** @brief Поиск пользователя в базе данных
    @details Данная функция делает выборку из базы данных 
    по имени пользователя и возвращает структуру с информацией  о нем. Ожидается, что соединение с базой
    данных установлено  и пользователь существует @param name Имя пользователя
    @return Информация о пользователе
    @throw DatabaseError Если произошла ошибка при подключении  к базе данных
    @throw InvalidRequest Если пользоваделя в базе не существует
 */
   CustomerPtr GetCustomer(const std::string& name);
};

Этого достаточно для 99% кода, но остается вопрос комментирования на уровне проекта, а не файла. Для этого служат следующие директивы:

@mainpage Основная страница проекта, то с чего начинается просмотр документации.

Пример:
@mainpage Приложение для учета пользователей
@page Дополнительные страницы проекта. Я рассматриваю их как логически обособленные части проекта.

Пример:
@page Database Database
Утилиты для работы с базой данных
@ref Ссылки на страницы проекта, с их помощью можно организовать например ссылки с главной страницы проекта на страницы подпроектов.

Пример:
@ref Database Утилиты для работы с базой данных

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

В свою очередь в корне проекта я также создаю файл description.h в котором немного рассказано о проекте в целом и приведены ссылки на его части:

/**
 @mainpage Приложение для учета пользователей Состоит из следующих частей:
 - @ref Database Утилиты для работы с базой данных
*/

Чтобы привести фрагмент кода (например пример работы с классом) используется конструкция @code@endcode:

/** Пример использования Foo:
 @code
 Foo f();
 f.Run();
 @endcode */

Списки можно создавать с помощью символа - (минус). Пример:

/** Список:
 - Первый пункт
 - Второй пункт
*/

Перечисления я оформляю так:

/** @brief Режимы устройства */
enum Mode
{
	Mode_On,	/**< Включено */
	Mode_Off	/**< Выключено */
};

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

Ложка дегтя

Обратите внимание! Очень ВАЖНО перед объявлением пространства имен повторять его имя в комментарии Doxygen. Похоже на баг (версия 1.6.2), но без этого содержимое пространства не попадет в документацию. Пример:

/** namespace A @brief Пространство имен A */
namespace A
{
}

Символ @ в конструкции namespace отсутствует!

Файл с настройками Doxygen

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

doxygen -g

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


Рассмотрю некоторые из пунктов которые я изменяю:

PROJECT_NAME Имя проекта.
QUIET Допустимые значения YES и NO. Я ставлю YES, для того чтобы предупреждения не терялись среди обилия информации о работе программы.
WARN_NO_PARAMDOC YES для вывода предупреждений о недокументированных аргументах функции.
INPUT Путь к анализируемому коду. Я рекомендую в корне проекта создать директорию docs содержащую Doxyfile с настройками, в таком случае значение этого параметра будет ../
FILE_PATTERNS Шаблон для имени анализируемых файлов. Поскольку документироваться должны только открытые интерфейсы, то очевидно, что следует анализировать только заголовочные файлы: *.h
RECURSIVE Рекурсивно анализировать файлы в поддиректориях – YES.
EXCLUDE_PATTERNS Исключение из анализа директорий соответствующих маске.

Пример:
EXCLUDE_PATTERNS = */.svn/* */build/* */tests/* */sources/*
HAVE_DOT YES, если установлен Graphviz и требуется визуализация связей.

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