2. Sphinx

Am Anfang (ca. 2002) war die Markup Sprache reStructuredText (reST). Das war eine Idee aus der Welt der Programmiersprache Python, um Dokumentation zu schreiben, die automatisch mit Hilfe von Umwandlungsprogrammen in Ausgabeformate wie HTML und PDF umgewandelt werden kann. Die Programme sind im Linux Paket docutils enthalten, man installiert sie mit apt install python3-docutils (es ist immer Python3 zu bevorzugen, da Python 2 im Jahr 2020 eingestellt wird). Wenn man eine Textdatei text.rst mit reStructuredText (Endung .rst) geschrieben hat, dann wandelt man diese nach HTML um mit dem Kommando:

rst2html text.rst text.html

Die Homepage der „docutils“ ist hier http://docutils.sourceforge.net/rst.html.

Mit dem Text Quick reStructuredText kann man sich schnell einen Überblick über die wichtigsten Markup Befehle verschaffen:

Eine wichtige Eigenschaft von reST ist seine Erweiterbarkeit. Neue Markup-Befehle können einfach definiert werden.

Das Sphinx Projekt hat diese Erweiterbarkeit genutzt und reST zu einer kompletten Textverarbeitung weiterentwickelt, mit der man beliebige Dokumente erstellen kann.

_images/sphinx-logo.png

Abb. 2.1 Die Homepage von Sphinx ist http://sphinx-doc.org

Sphinx gibt es auf Ihrem Linux-Rechner als Paket python3-sphinx.

Um eine Dokumentationsprojekt zu beginnen, legt man ein neues Unterverzeichnis an, z.B. dva-bericht, und ruft darin das Programm sphinx-quickstart auf. Nach ein paar ja/nein Abfragen (siehe Log) wird dadurch das Gerüst einer neuen Dokumentation generiert. Danach sind in dem Verzeichnis folgende Dateien:

dva-bericht/
   conf.py      <-- Konfiguration fuer Sphinx
   index.rst    <-- Die Hauptseite
   make.bat     <-- Zum Bauen der Doku unter Windows
   Makefile     <-- Zum Bauen der Doku unter Linux
   _static      <-- Hilfsverzeichnis, kann man ignorieren
   _templates   <-- Hilfsverzeichnis, kann man ignorieren

Mit sphinx-quickstart kann man auch herausfinden, welche Version von Sphinx man installiert hat, auf meinem Rechner erhalte ich folgende Ausgabe (die Version kann bei Euch eine andere sein):

$ sphinx-quickstart --version
sphinx-quickstart 2.2.0

Man sollte unter einer halbwegs aktuellen Version arbeiten, also z.B. 1.8.x oder 2.x.

Die „richtige“ Dokumentation in einem bestimmten Format muss erst erzeugt werden. Wenn man z.B. nach HTML ausgeben möchte, muss man eingeben: make html. Wenn man make ohne Option eingibt, bekommt man eine Liste aller Formate. Darunter sind html, latex, epub, man, texinfo, text, xml und viele weitere. Die erzeugten HTML Dateien findet man in _build/html. Zum Betrachten ruft man einen Web-Browser wie folgt auf:

firefox _build/html/index.html

Sollte das Dokument aus mehren Dateien bestehen, dann werden diese in index.rst inkludiert. Dieser Text besteht z.B. aus folgenden .rst Dateien: index.rst, schreibtipps.rst, sphinx.rst und werkzeuge.rst. Zum Inkludieren werden die drei Dateien ohne .rst Endung in index.rst aufgenommen:

.. toctree::
   :maxdepth: 2
   :numbered:     <-- die Abschnitte werden nummeriert

   intro          <--- intro.rst
   sphinx
   schreibtipps
   werkzeuge
   anpassen
   schluss

2.1. Überschriften

Die Überschriften kann man in verschiedenen Ebenen anlegen:

Ebene 1    <== Grosse Ueberschrift am Anfang in jeder .rst Datei
=======

Ebene 2
-------

Ebene 3
```````

Ebene 4
.......

2.1.1. Test Ebene 3

Die Nummerierung sollte 2.1.1 sein.

2.1.1.1. Test Ebene 4

Die Nummerierung sollte 2.1.1.1 sein. Mehr Ebenen braucht man nicht!

2.2. Querverweise

Man kann ganz einfach auf andere Abschnitte verweisen, wenn man vor dem Abschnitt mit:

.. _xyz:

ein „Label“ setzt. In diesem Fall hat das Label den Namen xyz. Vor den obigen Abschnitt „Überschriften“ habe ich das Label:

.. _ueberschriften:

gesetzt, somit kann ich nun auf diesen Abschnitt mit :numref:`ueberschrift` verweisen, der Link geht dann zu Abschnitt 2.1.

Ein anderer Link: In Abschnitt 2.5 kann man nachlesen, wie man Formeln in Sphinx schreibt. Das wurde gemacht durch :numref:`formeln`.

2.3. Ein wenig Markup

In Sphinx wird der in reStructuredText definierte Markup verwendet, plus zusätzlicher Markup, der nur in Sphinx existiert. Hier sind ein paar Beispiele:

Wörter können in verschiedenen Schriftarten geschrieben werden: fett, kursiv, Schreibmaschine.

Immer gut zu gebrauchen sind Postenlisten oder Aufzählungslisten:

  • Mikroprozessoren

    • STM32L476

    • AVR Mega32U4

  • Spannungsregler

    • 78L05

  • Widerstände

    • 470 Ω

    • 2,2 kΩ

Bei den Listen immer aufpassen, dass vor dem Beginn einer neuen Einrückung eine Leerzeile gemacht wird.

Eine Aufzählung:

  1. Aufstehen

  2. Zähne putzen

  3. Frühstücken

Wie man in der .rst Datei sieht, kann man ab dem zweiten Punkt die Zahl weglassen und stattdessen einfach #. schreiben.

Sehr vielseitig zu verwenden sind auch Code-Blöcke. Das sind Abschnitte, die in „Computerschrift“ gesetzt werden. Ein Beispiel:

Zeile 1
Zeile 2
Zeile 3

In der .rst Datei habe ich dazu geschrieben:

.. code-block:: text
   Zeile 1
   Zeile 2
   Zeile 3

Die Angabe hinter code-block:: bestimmt die Syntax, in der der folgende Abschnitt hervorgehoben wird. Mit text erfolgt keine Hervorhebung. Andere Möglichkeiten sind hier z.B. c, python, java, bash und so weiter.

Hier sind ein paar Beispiele:

/* c */
int main()
{
    printf("Hello World\n");
    return 0;
}

# python
L = []
for i in range(10):
   L.append(i)
   print(i)

# bash
if [ -f "/home/hhoegl/miniconda3/etc/profile.d/conda.sh" ]; then
     . "/home/hhoegl/miniconda3/etc/profile.d/conda.sh"
     CONDA_CHANGEPS1=false conda activate base
 else
     export PATH="/home/hhoegl/miniconda3/bin:$PATH"
 fi

Kommentare schreibt man in die .rst Dateien wie folgt:

.. Das ist ein Kommentar. Es erfolgt
   keine Ausgabe.

Alles weitere kann man der Sphinx Homepage ([SPHINX]) oder einem guten Tutorial entnehmen, z.B. [SPHTUT].

2.4. Programmtexte

Häufig müssen Sie Quelltextabschnitte in die Dokumentation aufnehmen. Sie können die Code-Blöcke entweder direkt in die .rst Datei aufnehmen oder aus einer Datei lesen:

Hier ist ein Beispiel für einen Codeabschnitt in Python:

# hallo.py
def hallo():
    print("Hallo Welt")
hallo()

und hier ist ein Abschnitt in C. Die Zeilennummern werden durch :linenos: aktiviert. Man kann auch den Beginn und die Schrittweite der Zeilennummern einstellen.

1
2
3
4
5
6
7
8
/* hallo.c */
#include <stdio.h>

int main()
{
    printf("Hallo Welt\n");
    return(0);
}

Wenn man den Quelltext aus einer Datei lesen möchte, dann geht das so (siehe die Datei serial.c):

.. literalinclude:: serial.c
   :language: c
   :linenos:

Hier ist die Ausgabe:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
#include <errno.h>
#include <fcntl.h> 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

void set_mincount(int fd, int mcount)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error tcgetattr: %s\n", strerror(errno));
        return;
    }

    tty.c_cc[VMIN] = mcount ? 1 : 0;
    tty.c_cc[VTIME] = 5;        /* half second timer */

    if (tcsetattr(fd, TCSANOW, &tty) < 0)
        printf("Error tcsetattr: %s\n", strerror(errno));
}


int main()
{
    char *portname = "/dev/ttyUSB0";
    int fd;
    int wlen;

    fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0) {
        printf("Error opening %s: %s\n", portname, strerror(errno));
        return -1;
    }
    /*baudrate 115200, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B115200);
    //set_mincount(fd, 0);                /* set to pure timed read */

    /* simple output */
    wlen = write(fd, "Hello!\n", 7);
    if (wlen != 7) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }
    tcdrain(fd);    /* delay for output */


    /* simple noncanonical input */
    do {
        unsigned char buf[80];
        int rdlen;

        rdlen = read(fd, buf, sizeof(buf) - 1);
        if (rdlen > 0) {
#ifdef DISPLAY_STRING
            buf[rdlen] = 0;
            printf("Read %d: \"%s\"\n", rdlen, buf);
#else /* display hex */
            unsigned char   *p;
            printf("Read %d:", rdlen);
            for (p = buf; rdlen-- > 0; p++)
                printf(" 0x%x", *p);
            printf("\n");
#endif
        } else if (rdlen < 0) {
            printf("Error from read: %d: %s\n", rdlen, strerror(errno));
        }
        /* repeat read to get full message */
    } while (1);
}

Nun noch ein Beispiel, das zeigt, wie Sie einen Link auf eine Quelltextdatei setzen. Im Verzeichnis sphinxbericht gibt es die Datei hello_world.py. Es funktioniert nicht, wenn man die Datei im aktuellen Verzeichnis verlinkt:

Der Grund ist, dass die Quelltextdatei nicht in den Ausgabeordner _build/html kopiert wird.

Die Datei sollte in den Ordner _static kopiert werden, dieser Ordner wird komplett in das Ausgabeverzeichnis kopiert.

2.5. Formeln schreiben

Formeln schreiben ist in Sphinx einfach: Man verwendet die gleiche Syntax wie in Latex!

Beispiel (Pythagoras): \(a^2 + b^2 = c^2\).

Die folgende Formel

\[ \begin{align}\begin{aligned}(a + b)^2 = a^2 + 2ab + b^2\\(a - b)^2 = a^2 - 2ab + b^2\end{aligned}\end{align} \]

wird so geschrieben:

.. math::

   (a + b)^2 = a^2 + 2ab + b^2

   (a - b)^2 = a^2 - 2ab + b^2

Damit man math verwenden kann, muss man die Sphinx Erweiterung sphinx.ext.mathjax in die Variable extensions in conf.py aufnehmen.

2.6. Literatur

SPHINX

Die Sphinx Homepage (besucht am 5.10.2019): https://www.sphinx-doc.org

YIU

Joseph Yiu, The Definitive Guide to the ARM Cortex-M3. 2nd edition, Newnes 2010.

SPHTUT

Thomas Cokelaer, Sphinx and RST syntax guide, 2014 (besucht am 5.10.2019) https://thomas-cokelaer.info/tutorials/sphinx/rest_syntax.html