Dokumentationen erstellen mit Doxygen

Eines der bekanntesten Software-Dokumentationswerkzeuge ist Doxygen. Zu seinen besonderen Stärken gehört die Einbindung von LaTeX. LaTeX ist besonders für Veröffentlichungen im naturwissenschaftlichen Sektor beliebt und ermöglicht die Darstellung von mathematischen Ausdrücken, physikalischen Formeln usw. Durch die Einbindung in Doxygen ist die Nutzung dieser Funktionalität in der Dokumentation möglich. Als Eingangsbeispiel zunächst ein Ausschnitt aus dem source code von OpenCV, genauer aus der base.hpp des core-Modules, die Dokumentation eines enums:

enum NormTypes {
                /**
                \f[
                norm =  \forkthree
                {\|\texttt{src1}\|_{L_{\infty}} =  \max _I | \texttt{src1} (I)|}{if  \(\texttt{normType} = \texttt{NORM_INF}\) }
                {\|\texttt{src1}-\texttt{src2}\|_{L_{\infty}} =  \max _I | \texttt{src1} (I) -  \texttt{src2} (I)|}{if  \(\texttt{normType} = \texttt{NORM_INF}\) }
                {\frac{\|\texttt{src1}-\texttt{src2}\|_{L_{\infty}}    }{\|\texttt{src2}\|_{L_{\infty}} }}{if  \(\texttt{normType} = \texttt{NORM_RELATIVE | NORM_INF}\) }
                \f]
                */
                NORM_INF       = 1,
                /**
                \f[
                norm =  \forkthree
                {\| \texttt{src1} \| _{L_1} =  \sum _I | \texttt{src1} (I)|}{if  \(\texttt{normType} = \texttt{NORM_L1}\)}
                { \| \texttt{src1} - \texttt{src2} \| _{L_1} =  \sum _I | \texttt{src1} (I) -  \texttt{src2} (I)|}{if  \(\texttt{normType} = \texttt{NORM_L1}\) }
                { \frac{\|\texttt{src1}-\texttt{src2}\|_{L_1} }{\|\texttt{src2}\|_{L_1}} }{if  \(\texttt{normType} = \texttt{NORM_RELATIVE | NORM_L1}\) }
                \f]*/
                 NORM_L1        = 2,
                 /**
                 \f[
                 norm =  \forkthree
                 { \| \texttt{src1} \| _{L_2} =  \sqrt{\sum_I \texttt{src1}(I)^2} }{if  \(\texttt{normType} = \texttt{NORM_L2}\) }
                 { \| \texttt{src1} - \texttt{src2} \| _{L_2} =  \sqrt{\sum_I (\texttt{src1}(I) - \texttt{src2}(I))^2} }{if  \(\texttt{normType} = \texttt{NORM_L2}\) }
                 { \frac{\|\texttt{src1}-\texttt{src2}\|_{L_2} }{\|\texttt{src2}\|_{L_2}} }{if  \(\texttt{normType} = \texttt{NORM_RELATIVE | NORM_L2}\) }
                 \f]
                 */
                 NORM_L2        = 4,
                 /**
                 \f[
                 norm =  \forkthree
                 { \| \texttt{src1} \| _{L_2} ^{2} = \sum_I \texttt{src1}(I)^2} {if  \(\texttt{normType} = \texttt{NORM_L2SQR}\)}
                 { \| \texttt{src1} - \texttt{src2} \| _{L_2} ^{2} =  \sum_I (\texttt{src1}(I) - \texttt{src2}(I))^2 }{if  \(\texttt{normType} = \texttt{NORM_L2SQR}\) }
                 { \left(\frac{\|\texttt{src1}-\texttt{src2}\|_{L_2} }{\|\texttt{src2}\|_{L_2}}\right)^2 }{if  \(\texttt{normType} = \texttt{NORM_RELATIVE | NORM_L2SQR}\) }
                 \f]
                 */
                 NORM_L2SQR     = 5,
                 /**
                 In the case of one input array, calculates the Hamming distance of the array from zero,
                 In the case of two input arrays, calculates the Hamming distance between the arrays.
                 */
                 NORM_HAMMING   = 6,
                 /**
                 Similar to NORM_HAMMING, but in the calculation, each two bits of the input sequence will
                 be added and treated as a single bit to be used in the same calculation as NORM_HAMMING.
                 */
                 NORM_HAMMING2  = 7,
                 NORM_TYPE_MASK = 7, //!< bit-mask which can be used to separate norm type from norm flags
                 NORM_RELATIVE  = 8, //!< flag
                 NORM_MINMAX    = 32 //!< flag
               };

Und hier die fertige Ausgabe von Doxygen:

Doxygen wird auch sehr gern im embedded-Sektor eingesetzt, bei vielen Herstellern von SoCs und Softwareprojekten. Hier als Beispiele Nordic Semiconductor (DEM Hersteller für Bluetooth SoCs), Espressif Systems, Raspberry Pi, Microchip, Zephyr usw.

Eine gute Dokumentation hängt primär vom Autor ab. Sie sollte den Leser aus der Vogelperspektive abholen und immer weiter ins Detail führen. Es sind dabei quasi zwei Welten zu verheiraten

  • Von Hand erstellte Texte mit anschaulichen Grafiken, welche den Leser von der Vogelperspektive in die Systemarchitektur immer tiefer führen
  • Aus dem dokumentierten source code autogenerierte Dokumentation von enums, Strukturen, Funktionen, Klassen, Namensräumen, Modulen etc.

Um diese zwei Welten zusammenzuführen, bietet Doxygen verschiedene Techniken an. Eine sehr elegante dieser Techniken ist die Verwendung von zum source code separaten Dokumentationsseiten in einem erweiterten Markdown Format. Es können aber ebenso auch HTML-Seiten etc. eingebunden werden. Dazu wieder ein Beispiel aus OpenCV. Die Einstiegsseite root.markdown.in der Dokumentation ist in Markdown geschrieben, hier allerdings noch als Vorlage zur Ergänzung durch CMake (CMake tauscht die hervorgehobenen CMake-Variablen vor Doxygen gegen Werte aus):

OpenCV modules {#mainpage}
==============

- @ref intro
- @ref tutorial_root
- @ref tutorial_py_root
@CMAKE_DOXYGEN_TUTORIAL_JS_ROOT@
@CMAKE_DOXYGEN_TUTORIAL_CONTRIB_ROOT@
- @ref faq
- @ref citelist

@CMAKE_DOXYGEN_MAIN_REFERENCE@

@CMAKE_DOXYGEN_EXTRA_REFERENCE@

Und hier wieder das Ergebnis (Ausschnitt), vollständig unter https://docs.opencv.org/4.9.0/index.html:

Nun zu einem Schnelleinstieg für Doxygen. Folgende Tools sollten installiert werden:

  • Doxygen
  • LaTeX
  • Graphviz

Graphviz dient dabei der automatischen Generierung von Abhängigkeitsgraphen.

Im Falle von Windows empfiehlt sich dabei:

Dabei sollte LaTeX besonders zu Beginn so konfiguriert werden, dass es ohne Nachfrage fehlende Pakete nachinstallieren darf. Beim ersten Durchlauf von Doxygen werden so alle nötigen Pakete nachinstalliert.

Sollte unter Visual Studio entwickelt werden, so leistet das Tool Atomineer Pro Documentation wertvolle Dienste. Es erstellt automatisch aus dem zu dokumentierenden source code sehr gute Vorlagen für die Dokumentation. Ein weites nützliches Tool ist HTML Help Workshop. Dieses erstellt aus HTML-Seiten eine Compressed HTML Help Datei (CHM). Leider wird dieses Tool von Microsoft nicht mehr gepflegt und auf den offiziellen Seiten von Microsoft existieren nur noch tote Links. Daher ist die Beschaffung schwierig, es existieren diverse Kopien auf Seiten mit dem verbundenen Risiko.

Unter Linux liegen alle Tools bei den Distributionen meist ohne Probleme in den Paketverwaltungen vor. Für LaTeX empfiehlt sich hier das Paket texlive. Das Paket texlive installiert dabei im Gegensatz zu texlive-full zunächst nur die Basisinstallation. Ich empfehle diesen Weg, beim ersten Durchlauf von Doxygen können dann alle nötigen Pakete wieder nachinstalliert werden.

Tipp: nach meiner Erfahrung sollte man mit dem coden, der Erstellung der Dokumentation für Doxygen und der source code Dokumentation gleichzeitig starten. Gerade das Dokumentieren aus der Vogelperspektive in den Markdown-Dokumenten hilft der eigenen Klarheit beim coden und bringt durch Selbstreflexion immer neue Ideen, Gedanken und hilft Logikfehler schneller zu finden. Weiter wird so die Verzahnung zwischen Vogelperspektive durch das beidseitige Annähern von Markdown-Dokumenten und source code Dokumentation verbessert.

Doxygen arbeitet „konsolenbasiert“:

doxygen <Konfigurationsdatei>

Damit ist doxygen in Skripte und Automatisierungsprozesse einbindbar. Eine Vorlage der Konfigurationsdatei kann mit

doxygen -g <Konfigurationsdatei>

erstellt werden. Gerade für den Einstieg ist stattdessen der Doxywizard zu empfehlen. Dieser ist eine GUI-Anwendung und erlaubt die interaktive Erstellung der Konfigurationsdatei.

Mindestens eingestellt sein müssen Projektname, das Verzeichnis der Quellen und das Ausgabeverzeichnis der Dokumentation. Es empfiehlt sich, die Verzeichnisse relativ zur Konfigurationsdatei anzugeben. So behält die Konfigurationsdatei auch z.B. in der Quellcodeverwaltung ihre Gültigkeit. Die Pfadangabe kann im Unix-Stil erfolgen.

Unter „Mode“ wird die Sprache des source codes eingestellt:

Unter „Output“ wird das Formt der Ausgabe eingestellt. Für Windows kann, wenn HTML Help Workshop installiert ist, eine Ausgabe für CHM vorbereitet werden. Sehr zu empfehlen ist wie bereits geschildert die Einbindung von LaTeX.

Wurde GraphViz installiert, so sollte dessen Funktionalität für die Erstellung von Abhängigkeitsgraphen genutzt werden:

Als Input können nun sowohl Verzeichnisse als auch Dateien angegeben werden. Diese können sowohl kommentierter source code als auch die Dokumentation ergänzende Markdown-Dateien umfassen. Wichtig: die richtige Codierung von source code und Markdown-Dateien muss in INPUT_ENCODING eingetragen werden, besonders bei Verwendung von Umlauten etc. Unter Visual Studio wird standartmäßig die Codierung iso-8859-1 verwendet. Es ist aber auch möglich, die Codierung mittels Tools wie Notepad++ zu ändern.

Jetzt wird es Zeit, sowohl mit der Dokumentation in Markdown als auch in source code zu beginnen. Doxygen ergänzt Markdown vielfältig um Funktionalitäten, hier kann nur ein Ausschnitt gezeigt werden. Eine Übersicht zu den Markdown-Funktionalitäten von Doxygen findet sich hier.

Wie bei jedem Text wird in den Markdown-Dokumenten viel mit Überschriften gearbeitet. Im Unterschied zum reinen Markdown-Standard lassen sich diese bei Doxygen referenzieren. Hierzu wird ein Header ID Attribut benötigt. Es dient als Anker. Wie die main-Funktion bei C und C++ für den Einstieg in den Programmcode vorbestimmt ist, so ist dies bei Doxygen das Header ID Attribut mainpage:

Einführung {#mainpage}
======================

Einer der wichtigsten Befehle im Markdown ist ref. Mit ihm werden Verweise auf benannte Symbole, Dateien, Abschnitte, Seiten oder Anker erstellt, und damit auch zum dokumentierten source code.

ACHTUNG: leider arbeitet die Verlinkung zumindest bisher (Doxygen Version 1.10.0) nicht bei .Net-Elementen, welche internal deklariert sind. Das macht die Dokumentation eines Programms unter dem Fokus seine Systemarchitektur zu erklären leider schwer.

Sehr nützlich sind Befehle wie todo, bug, deprecated etc. Mit ihnen lassen sich im source code an den jeweiligen Stellen verteilt Kommentare hinterlassen und diese werden von Doxygen dann in Listenform zusammengefügt. So hilft das Tool, die Übersicht zu behalten.

Ebenfalls sehr nützlich ist die Unterstützung von PlantUML. Dieses unterstützt neben verschiedenen UML-Diagrammtypen gerade auch eine ganze Liste von anderen Diagrammarten wie etwa Netzwerktopologien.

Tip: gerade von gut geführten Softwareprojekten wie OpenCV und den BSPs diverser Hersteller von SoCs lassen sich viele gute Praktiken und der Einsatz von Doxygen-Befehlen für gute Dokumentationen abschauen. Es lohnt sich daher, diese „im Inneren“ zu studieren.

wird fortgeführt …