Doxygen - automatische Dokumentation von Quellcode

Top1 Überblick

Doxygen ist ein Freeware-Tool zur automatischen Dokumentation von Programmquellcode. Es werden verschiedene Programmiersprachen unterstützt (z.B. C++, C#, PHP, Java,...).

Grundprinzip:
Aus Kommentaren im Quellcode und aus allgemeinen sprachspezifischen Strukturbeziehungen (z.B. Vererbung, Verwendung, Include) wird ein HTML-Dokument generiert.

Top2 Installation und Vorbereitung

Top2.1 Download

Download als Freeware unter http://www.doxygen.org/.

Zusätzlich zur Darstellung von Diagrammen (z.B. Vererbung, Abhängigkeit):

  • Download der Grafik-Komponente Dot.exe: http://www.graphviz.org/
  • Durch die Installation sollte Dot.exe im Suchpfad PATH ergänzt werden. Ggf. Visual Studio neu starten, damit die geänderte Pfadeinstellung wirksam wird.
  • Im Configfile für Doxygen muss folgender Eintrag vorliegen: "HAVE_DOT = YES"

Top2.2 Konfiguration

Doxygen bietet zahlreiche Einstellmöglichkeiten, die in einem Konfigurationsfile gesetzt werden können.
Als Ausgangspunkt für die Erstellung eines eigenen Konfigurationsfiles empfiehlt sich eine von Doxygen selbst generierte Vorlage mit der Auflistung aller in der aktuellen Version möglichen Einstelloptionen und ausführlichen Erläuterungen im Kommentarformat. Die Erzeugung dieses Konfigurationsfiles erfolgt über:
Doxygen -g MyDoxygenConfigfile.dxy
Ist das Konfigurationsfile erstellt und an die eigenen Vorstellungen angepasst, so erfolgt die Erzeugung der Dokumentation über:
Doxygen MyDoxygenConfigfile.dxy
Eine detaillierte Beschreibung aller Optionen findet sich unter http://www.stack.nl/~dimitri/doxygen/manual.html.

Top2.2.1 Vorgehensmodell für die Dokumentation

Als ein sinnvolles Vorgehensmodell für die Dokumentation von Quellcode erscheinen folgende Prinzipien:
  • nur sinnvolle Kommentare - Vermeidung trivialer Dokumentation
    Kommentare nur dann zu Klassen, Funktionen, Parametern und Attributen ergänzen, wenn das betreffende Objekt für das Verständnis wichtig ist und der Kommentar mehr Information liefert als der reine Elementname bereits beinhaltet.
  • Quellcode-Autor entscheidet über Aufnahme in Dokumentation
    Grundsätzlich werden bei der Generierung der Dokumentation alle Quellcode-Teile in die Dokumentation aufgenommen.
    Der Quellcode-Autor kann jedoch beliebige Codeabschnitte von der Aufnahme in die Dokumentation ausschliessen. Mit /cond ... /endcond kann er hierzu z.B. private Klassenanteile, unwichtige Helperklassen, Makros oder den gesamten Implementierungscode von der Dokumentation ausschliessen.
Ausgehend von diesem Modell ergeben sich die im folgenden Abschnitt aufgeführten Konfigurationseinstellungen.

Top2.2.2 Wichtige Einstellungen im Konfigfile

  • Aufnahme aller Quellcode-Anteile in die Dokumentation
    EXTRACT_PRIVATE        = YES
    EXTRACT_STATIC         = YES
    EXTRACT_LOCAL_CLASSES  = YES
    
  • Doxygen erzeugt Warnhinweise für den Generiervorgang. Undokumentierte Quellcode-Elemente werden jedoch ausdrücklich zugelassen.
    QUIET                  = NO
    WARNINGS               = YES
    WARN_IF_UNDOCUMENTED   = NO
    
  • Die Sortierung der Beschreibungen zu den Objekten soll der Deklarationsreihenfolge im Quellcode entsprechen
    SORT_MEMBER_DOCS       = NO
    SORT_BRIEF_DOCS        = NO
    
  • Meist wird lediglich die Dokumentation im HTML-Format mit einem Navigationsbaum am linken Seitenrand benötigt.
    GENERATE_HTML          = YES
    GENERATE_TREEVIEW      = YES
    GENERATE_LATEX         = NO
    
  • Sinnvolle Einstellungen zur Erzeugung von Diagrammen
    HAVE_DOT               = YES
    CLASS_DIAGRAMS         = YES
    HIDE_UNDOC_RELATIONS   = NO
    COLLABORATION_GRAPH    = YES
    
  • Quellcode wird mit allen Kommentaren angezeigt
    STRIP_CODE_COMMENTS    = NO
    

Top3 Einbindung in Visual-Studio als externes Tool

Der Aufruf der Dokumentgenerierung kann bequem als "externes Tool" in das Visual Studio eingebunden werden.

DoxyGen generiert Fehler und Warnungen. Um zu diesen Ausgaben das automatische Springen zur betreffenden Stelle im Quellcode zu ermöglichen, sollte folgender Eintrag im Doxygen-Konfigfile vorhanden sein:

WARN_FORMAT = "$file($line) : $text"

Top3.1 Projekspezifisches Config-File

Über einen einheitlichen Menueintrag kann zur jeweils geöffneten Solution die Doxygen-Dokumentation auf Knopfdruck generiert werden.
  • Eintrag als externes Tool unter ExtrasExternal tools definieren
    • Title: Doxygen
    • Command: C:Programmedoxygenbindoxygen.exe (Installationspfad)
    • Arguments: $(SolutionDir)DoxyFile.dxy (Config-File)
    • activate "use output window"
  • Konfigfile mit Namen "DoxyFile.dxy" parallel zur Solution (.sln) anlegen.

Top3.2 Generisches Config-File

In größeren Projekten mit mehreren Teilprojekten kann ein einheitliches allgemeines Configfile eingesetzt werden. Will man zu einem Teilprojekt die Dokumentation generieren, so wird dieses Teilprojekt als Startup-Projekt gesetzt und anschliessend das Externe Tool aufgerufen:
  • Title: Doxygen (generic)
  • Command: C:MySpecificPathGenerateDocumentation.cmd
    oder auch mit Umgebungsvariablen: %MY_SOURCE_DIR%MyResourcesGenerateDocumentation.cmd
  • Arguments: $(ProjectDir) $(ProjectFileName)
  • activate "use output window"
Das folgende Commandfile "GenerateDocumentation.cmd" erzeugt aus dem allgemeinen Configfile und dem ausgewählten Projekt ein angepasstes Configfile, das Doxygen zur Bearbeitung übergeben wird. Quell- und Zielpfade können dabei aus Umgebungsvariablen übernommen werden.
::--------------------------------------------------------------
::
:: File: GenerateDocumentation.cmd
:: 
:: Generate Doxygen documentation for a SW project
:: Syntax: GenerateDocumentation [ProjectPath] [ProjectName]
::
:: Remark: Syntax when used as external tool within Visual Studio:
:: GenerateDocumentation $(ProjectDir) $(ProjectFileName)
::
::--------------------------------------------------------------

@echo off
setlocal

set thisFile=GenerateDocumentation.cmd
set relativePathToDoxyGenFiles=BuildAll
set nameGenericDoxyFile=DoxygenTemplateForDocGeneration.dxy
set nameSpecificDoxyFile=GeneratedDoxyFileDoNotEditManually.dxy
set nameDoxygenOutputDir=DoxyGenDoc

::-----  Command line params  -----

echo Generating DoxyGen documentation... (%thisFile%))
echo.

:: remove quotes
set inputProjectDir=%~1%
set inputProjectName=%~2%

echo ProjectDir from cmd line   : %inputProjectDir%
echo ProjectName from cmd line  : %inputProjectName%

if "%inputProjectDir%" == "" goto :ERR_MISSING_CMDLINE_PARAMS
if "%inputProjectName%" == "" goto :ERR_MISSING_CMDLINE_PARAMS

:: cut trailing ""
set projectDir=%inputProjectDir:=%

:: cut extension from project file name
for /f "tokens=1,2 delims=/." %%a in ("%inputProjectName%") do set projectName=%%a

echo ProjectDir                 : %projectDir%
echo ProjectName                : %projectName%


::-----  Get specific environment paths  -----

set sourceDir=%MY_SOURCE_DIR%
:: cut trailing ""
set sourceDir=%sourceDir:=%
if "%sourceDir%" == "" goto :ERR_SOURCE_DIR_NOT_SET
set sourceDrive=%sourceDir:~0,2%
echo SourceDir from env         : %sourceDir%

set targetDir=%MY_TARGET_DIR%
:: cut trailing ""
set targetDir=%targetDir:=%
if "%targetDir%" == "" goto :ERR_TARGET_DIR_NOT_SET
set targetDrive=%targetDir:~0,2%
echo TargetDir from env         : %targetDir%

echo SourceDrive                : %sourceDrive%
echo TargetDrive                : %targetDrive%


::-----  Create target directory  -----

%targetDrive%
cd %targetDir%
if not exist %nameDoxygenOutputDir% (
mkdir %nameDoxygenOutputDir%
)
set targetDir=%targetDir%%nameDoxygenOutputDir%
if not exist %targetDir% goto :ERR_TARGET_DIR_NOT_CREATABLE
cd %targetDir%
if not exist %projectName% (
mkdir %projectName%
)
set targetDir=%targetDir%%projectName%
echo TargetDir                  : %targetDir%
if not exist %targetDir% goto :ERR_TARGET_DIR_NOT_CREATABLE


::-----  Build names for doxy files  -----

set doxyTemplatePath=%sourceDir%%relativePathToDoxyGenFiles%%nameGenericDoxyFile%
:: replace "" with ""
set doxyTemplatePath=%doxyTemplatePath:=%
echo Generic doxy template      : %doxyTemplatePath%

set nameSpecificDoxyFile=%targetDir%%nameSpecificDoxyFile%
:: replace "" with ""
set nameSpecificDoxyFile=%nameSpecificDoxyFile:=%
echo Specific doxy file         : %nameSpecificDoxyFile%


::-----  Generating specific doxy config file  -----

echo.
echo Generating specific doxy file %nameSpecificDoxyFile% ...
set d="%nameSpecificDoxyFile%"
echo # Automatically generated file > %d%
echo # DO NOT EDIT MANUALLY! >> %d%
echo. >> %d%
echo # BEGIN ------- Contents of generic doxy file >> %d%
type %doxyTemplatePath% >> %d%
echo # END   ------- Contents of generic doxy file >> %d%
echo. >> %d%
echo # BEGIN ------- Project specific settings >> %d%
echo PROJECT_NAME           = "%projectName%" >> %d%
echo OUTPUT_DIRECTORY       = "%targetDir%" >> %d%
echo # END  -------- Project specific settings >> %d%


::-----  Generating docoment  -----

pushd .

echo.
echo ----- doc generation in progress ...
echo.
echo scanning for contents in directory:
%sourceDrive%
cd %projectDir%
cd

echo.
echo calling doxygen %nameSpecificDoxyFile% ...
doxygen.exe "%nameSpecificDoxyFile%"
echo.
echo ----- doc generation finished  -----
popd
endlocal
goto :eof


::-----  Error handling  -----

:ERR_MISSING_CMDLINE_PARAMS
echo ERROR: Expected calling syntax with params: projectDir projectName
echo Hint: When called from Visual Studio Tools menu you can use:
echo $(ProjectDir) $(ProjectFileName)
goto :TERMINATE_WITH_ERROR

:ERR_SOURCE_DIR_NOT_SET
echo ERROR: Source dir MY_SOURCE_DIR not set as environment variable!
goto :TERMINATE_WITH_ERROR

:ERR_TARGET_DIR_NOT_SET
echo ERROR: Target dir MY_TARGET_DIR not set as environment variable!
goto :TERMINATE_WITH_ERROR

:ERR_TARGET_DIR_NOT_CREATABLE
echo ERROR: Target dir %targetDir% could not be created
goto :TERMINATE_WITH_ERROR

:TERMINATE_WITH_ERROR
echo.
echo ...%thisFile% terminated with error
goto :eof

Top4 Kommentierung im Quellcode

Top4.1 Grundsätzliche Empfehlungen

Die Dokumentation sollte sich auf das Wesentliche beschränken. Oft sind Realisierungsdetails wie Methodenimplementierungen und private Methoden und Daten für eine Überblicksdokumentation nicht erwünscht. Derartige Quellcode-Bereiche können über die Marker cond und endcond von der Aufnahme in die Dokumentation ausgenommen werden.

Folgende Elemente sollten in jedem Fall in die Dokumentation aufgenommen werden:

  • Beschreibung zur Klasse selbst
  • alle public-Methoden und Interfaces
  • include-Anweisungen, hierzu sind keine manuellen Kommentare notwendig, Doxygen erzeugt vielmehr automatisch Diagramme zu den Include-Abhängigkeiten
  • wichtige Makros und Typdefinitionen

Nach Bedarf können folgende Elemente zusätzlich aufgenommen werden:

  • Usage-Dokumentation
    Für wichtige Klassen ein eigener Abschnitt mit Erläuterungen und Beispielen zur Verwendung. Der Abschnitt könnte z.B. im Rahmen der Headerdokumentation zur Hauptklasse aufgenommen werden.
  • wichtige private-Anteile, sofern sie zum Verständnis des Gesamtsystems beitragen

Top4.2 Beispiel für einen Klassenheader

////////////////////////////////////////////////////////////////////////////////
///
/// file
/// author Gerald Fahrnholz
/// 
/// defgroup GrpRunner Using Runner                                 (-1-)
/// @{
/// ingroup GrpTestUtils                                            (-2-)
/// brief
/// Class Runner executes a user defined set of test functions
/// containing arbitrary test cases.
///
/// The test functions have to be declared within
/// macros ref TTB_TEST_FUNC or ref TTB_TEST_FUNC_DESC.
///
/// <b>n Supported features</b>
///
/// - provides a controlling main function, users have only to define the
///   test functions (similar to automatic test cases within Boost::Test)
///
/// - possibility to select and sort the test functions for execution
///
/// ...
///
/// sa Runner.h (File documentation)                                (-3-)
/// sa TestToolBox::Runner (class documentation)
/// 
////////////////////////////////////////////////////////////////////////////////

#include "TestToolBox/TestEvents.h"


/// brief This class runs user defined test functions and organizes
/// their execution.                                                 (-4-)
///
/// nosubgrouping 
/// sa Runner.h (File documentation)
/// sa GrpRunner (Usage documentation)
class Runner
{
public:

    /// Automatically execute the defined test functions             (-5-)
    int Execute(RunnerExtensionPoints* in_pExtensionPoints = 0);

private:
    ///cond                                                         (-6-)

    // some not interesting details
    
    ///endcond
};

/// @} // endGrpRunner
Erläuterungen

(-1)
Die Usage-Dokumentation wird über defgroup als separate Gruppe mit dem eindeutigen Namen "GrpRunner" und dem Titel "Using Runner" definiert. Der dazugehörige Bereich wird über "@{" und "@}" festgelegt.

(-2-)
Über ingroup wird die Usage-Dokumentation einer hierarchisch höherstehenden Gruppe zugeordnet. In der generierten Dokumentation wird diese logische Gliederung der Usage-Dokumentationen als Baumstruktur unterhalb "Modules" angezeigt.

(-3-)
Es ist hilfreich auf die unterschiedlichen Dokumentationsarten zu einem Thema zu verweisen. Typischerweise gibt es stets eine File- und eine Klassendokumentation.

(-4-)
Hier beginnt die Klassendokumentation. Neben einer kurzen Beschreibung solte auch hier ein Verweis auf die File-Dokumentation und eine evtl. existierende Usage-Dokumantation erfolgen. In der generierten Dokumentation werden alle Klassen unterhalb "ClassList" in einer flachen Liste aufgeführt.

(-5-)
Eine Kurzbeschreibung zu einer Methode kann ohne besondere Kennzeichnung in einer Textzeile erfolgen. Werden mehrere Zeilen benötigt, so ist "brief" voranzustellen

(-6-)
Weniger interessante Klassenanteile werden über cond ausgeblendet

Top4.3 Formatierungen

Zur Formatierung können HTML-Konstrukte innerhalb der Kommentartexte eingesetzt werden. Um die Lesbarkeit des Quellcodes nicht zu gefährden, sollte aber nur sparsam davon Gebrauch gemacht werden. Meist wird der Kommentar häufiger im Quellcode als in der generierten Dokumentation gelesen.

Kurzbeschreibungen, brief
Der Kommentartext zu einem dokumentierten Element gliedert sich in eine Kurzbeschreibung und in eine erweiterte Gesamtbeschreibung, die in der erzeugten Dokumentation auch separat aufgeführt werden. Für einfache Sachverhalte sollte lediglich eine Kurzbeschreibung angelegt werden, da dann auch im generierten Dokument unnötige Textabschnitte vermieden werden.

Ein Text wird nach folgenden Regeln als Kurzbeschreibung identifiziert:

  • Es gibt nur eine Kommentarzeile
  • die erste Kommentarzeile, die durch eine leere Kommentarzeile von der weiteren Beschreibung abgesetzt ist
  • der erste durch "brief" eingeleitete (evtl. mehrzeilige) Absatz, der durch eine leere Kommentarzeile von der weiteren Beschreibung abgesetzt ist

Aufzählungen über "-"
Einfache Strich-Aufzählungen können über "-" und einen auch mehrzeiligen Textabsatz definiert werden. Untergeordnete Aufzählungspunkte werden über eingerückte Strichzeilen definiert. Größere Abstände zwischen den Aufzählungen können über einfache Leerzeichen (Absätze) erreicht werden, allerdings wird danach stets mit Aufzählungsebene 1 fortgesetzt.

Beispiel:

/// - Aufzählungspunkt 1
/// - Aufzählungspunkt 2
///   - Unterpunkt 2.1
///   - Unterpinkt 2.2
/// - Aufzählungspunkt 3
///   dies gehört auch noch zur Aufzählung

Fettschrift <b>
Abschnittsüberschriften oder wichtige Ausdrücke innerhalb des Textes können über das HTML-Tag <b> als Fettschrift formatiert werden. Um bei Überschriften einen zusätzlichen Abstand einzufügen kann dem Text einfach "n" vorangestellt werden.

Code-Beispiele code ... endcode
Mehrzeilige Codebeispiele können über code ... endcode definiert werden

direkte http-Hyperlinks
können ohne besondere Formatierung direkt im Text verwendet werden

ref, Referenz
Hyperlink auf anderswo im Dokument beschriebenes Thema, Textformat wird nicht beeinflusst

sa, "see also"
abgesetzter Block mit Verweisen (Hyperlinks) auf anderswo im Dokument beschriebene Themen

Top5 Minimalistische Dokumentation ausgewählter Aspekte

Eine in Entwicklerkreisen häufig anzutreffende Argumentation besagt, dass die einzig gültige Dokumentation der Quellcode ist, da nur dieser stets aktuell ist. In manchen Fällen mag es sich dabei um eine bequeme Ausrede handeln, um von lästigen Dokumentationsarbeiten verschont zu bleiben, aber im Kern steckt darin das Problem, dass man sich nie sicher sein kann, dass eine vorliegende Dokumentation nicht einen längst veralteten Implementierungsstand beschreibt.

Trotz dieser Unsicherheiten kann es in größeren Projekten hilfreich und notwendig sein, zumindest zur Beschreibung von Basisdiensten (Frameworks) und wichtigen Komponenten-Schnittstellen eine erläuternde und aktuelle Dokumentation zur Verfügung zu haben. Zielsetzung ist dabei die Unterstützung des korrekten Einsatzes der beschriebenen SW-Anteile innerhalb des Projektteams.

Zur Erreichung dieses Ziels kann Doxygen entsprechend folgender Prinzipien konfiguriert werden:

  • Beschränkung auf explizit ausgewählte Quellcode-Dateien und Code-Abschnitte
    Im Gegensatz zu dem weiter oben beschriebenen Ansatz, bei dem grundsätzlich alle Quelldateien für die Dokumentation herangezogen werden und man aktiv durch Anweisungen im Quellcode unerwünschte Abschnitte ausblenden muss, kann man auch nach dem umgekehrten Prinzip verfahren: nur speziell konfigurierte Quellcodebestandteile werden in die Dokumentation aufgenommen.
  • Aktuelle Beschreibung durch Einbindung von compilierbarem Code
    Im Quellcode können innerhalb von Doxygen-Kommentaren Codebeispiele aufgeführt werden. Da sie sich innerhalb des Kommentarbereiches befinden, werden sie vom Compiler ignoriert und es besteht die Gefahr, dass sie nicht mehr dem Implementierungsstand entsprechen. Es ist jedoch auch möglich derartige Codebeispiele direkt aus dem vom Compiler (z.B. im Rahmen eines UnitTests) übersetzten Quellcode zu entnehmen. Mit dieser Vorgehensweise ist garantiert, dass unter der Voraussetzung einer zeitnahen Neugenerierung der Doxygen-Dokumentation alle Code-Beispiele aktuell bleiben.
  • Selbst erstellte Indexseite
    Um den Nutzer möglichst schnell auf relevante Informationen oder aktuelle Änderungen verweisen zu können, kann eine eigene Startseite für die Dokumentation erstellt werden, die zusätzlich zu den von DoxyGen automatisch erstellten Hyperlink-Listen gleich als Einstiegsseite aufgeblendet wird.

Top5.1 Auswahl zu dokumentierender Dateien

Im DoxyFile können über das Tag INPUT sowohl ganze Verzeichnisse als auch nur einzelne Dateien für die Generierung der Dokumentation ausgewählt werden. Die Pfade sind relativ zum Speicherort des DoxyFiles anzugeben.

Im nachfolgenden Beispiel werden alle Dateien aus dem Verzeichnis "FolderA" und die Dateien "ClassB2.h" und "ClassB4.h" aus "FolderB" ausgewählt:

INPUT = ../FolderA 
        ../FolderB/ClassB2.h
        ../FolderB/ClassB4.h
Innerhalb der ausgewählten Dateien können wie weiter oben beschrieben nicht zu dokumentierende Code-Abschnitte über "cond / endcond" ausgeblendet werden:
// file ClassB2.h
... 
///cond

// some not interesting details
    
///endcond

Top5.2 Einbindung echten Quellcodes in die Dokumentation

Zunächst sind die Verzeichnisse mit den in Frage kommenden Beispieldateien oder eine Liste der Dateien selbst anzugeben. Im einfachssten Fall gibt man nur ein Wurzelverzeichnis an, unter dem sich evtl. auch erst in tieferen Hierarchien die Datein mit den Codebeispielen befinden. Pfade sind wiederum relativ zum Speicherort des DoxyFiles anzugeben:
EXAMPLE_PATH = ../
Innerhalb beliebiger DoxyGen-Kommentare können dann Codefragmente aus anderen Dateien eingebunden werden:
// File: SomeSourceFile.h/.cpp

/// This is the DoxyGen documentation for ...
///
/// Code snippet to demonstrate usage of class ADerived:
/// \snippet UnitTest/TestClassADerived.cpp access ADerived
Der zugehörige Quellcode in UnitTest/TestClassADerived.cpp muss dann einen Codeabschnitt mit der Kennnzeichnung "access ADerived" als Start und End-Tag enthalten:
// File: TestClassADerived.cpp
...

void SomeOtherFunction()
{
    /// [access ADerived]
    ADerived* pAd = new ADerived;
    pAd->DoIt();
    delete pAd;
    /// [access ADerived]
}
Die generierte Dokumentation sieht dann in etwa folgendermaßen aus:

This is the DoxyGen documentation for ...

Code snippet to demonstrate usage of class ADerived:

    ADerived* pAd = new ADerived;
    pAd->DoIt();
    delete pAd;

Die Codebeispiele können dabei auch aus Dateien stammen, die gar nicht für die Generierung der Dokumentation ausgewählt sind.

Top5.3 Selbst erstellte Indexseite als Einstieg

Es kann eine eigene Datei MainPage.h angelegt werden, die den ausschliesslichen Zweck verfolgt, für die Einstiegsseite zur Dokumentation Inhalte und Struktur zur Verfügung zu stellen. Diese Datei muss ebenfalls im Doxyfile unter INPUT aufgelistet sein:
INPUT = ..
        ../doc/MainPage.h
Innerhalb der Datei MainPage.h muss über das Tag "\mainpage" deklariert werden, dass die Inhalte auf die Einstiegsseite generiert werden.
/// File: MainPage.h

/// \mainpage SW component X
///
/// Short introduction to SW component X
/// ...
/// (all DoxyGen tags can be used here to directly enter
///  infos or to refer to information in other files)