Accessibility Tools

- Le blog participatif de bioinformatique francophone depuis 2012 -

Je me suis amusé à écrire une mini librairie C++ pour l'algèbre linéaire (matrices et vecteurs, …) et j'ai eu envie de générer une documentation à partir du code source.

Je me suis renseigné un peu et j'ai découvert Doxygen, un outil écrit en C++ qui permet de générer de la documentation à partir des codes sources de différents langages (C++, Java, etc.).

Cela fonctionne bien, mais la sortie HTML n'est vraiment pas terrible. J'ai donc décidé, avec l'avis du forum Discord de l'association des Jeunes Bioinformaticiens de France (JeBif) de me pencher sur Sphinx, un générateur de documentation Python, qui produit des sites plus élégants.

Sphinx ne permet pas d'extraire la documentation des codes sources C++, mais génère du HTML à partir de fichiers au format ReStructuredText ou Markdown. Je continue ainsi d'utiliser Doxygen pour l'extraction des commentaires et des signatures des fonctions. Avec l'extension Breathe de Sphinx, on peut en effet extraire la documentation générée au format XML par Doxygen pour en faire un jolie petite documentation dans le style ReadTheDoc.

Prêt ? Alors c'est parti !

Comment configurer Doxygen ?

Commençons tout d'abord par installer Doxygen :

sudo apt-get install doxygen

Nous allons nous baser pour notre exemple sur un projet fictif : une classe C++ représentant un chat.

mkdir -p monprojet/include
cd monprojet/include

Créons maintenant un header C++, avec quelques blocs de commentaires :

// monprojet/include/cat.hpp

/**
 * @brief This is a cat
 */
class Cat {
public:
    Cat() {
        say("I'm a cat");
    }
    /**
     * @brief the cat is saying meow 
     */
    void meow()
    {
        say("meow");
    }

    /**
     * @brief the cat is saying something 
     * @param message the message to say
     */
    void say(const std::string& message)
    {
        std::cout << message << std::endl;
    }
};

On peut maintenant générer le fichier de configuration de doxygen à la racine du projet :

doxygen -g Doxyfile

On peut changer quelques paramètres dans ce fichier, dont notamment INPUT, le dossier où se trouve les sources, et surtout GENERATE_​XML pour obtenir les fichiers xml nécessaire à Sphinx.

INPUT = "./include"

EXTRACT_ALL = YES

RECURSIVE = YES

OUTPUT_DIRECTORY  =  "./doc/"

GENERATE_XML = YES

On peut aussi désactiver les sorties HTML et LaTeX de doxygen, activées par défaut et qui ne nous sont pas utiles.

GENERATE_HTML = NO

GENERATE_LATEX = NO

Voilà, c'est fini pour la configuration du Doxyfile, maintenant, c'est au tour de Sphinx !

Comment configurer Sphinx ?

Pareillement, il faut installer Sphinx, par exemple avec le gestionnaire de paquet de votre distribution Linux (on pourrait aussi utiliser Pip):

sudo apt-get install python-sphinx

Dans le dossier du projet, on peut maintenant créer un dossier doc qui contient la configuration Sphinx et la documentation générée :

mkdir doc
cd doc

Dès lors on peut utiliser sphinx-​quickstart pour générer la structure de la documentation Sphinx :

sphinx-quickstart

Il faut maintenant configurer Sphinx afin qu'il puisse importer le xml de doxygen.

En premier lieu, il faut installer Breathe, une extension qui permet justement d'importer la doc depuis les fichiers xml :

pip install breathe

Dans le fichier conf​.py , on doit ajouter ces quelques lignes :

# in doc/source/conf.py
extensions = ['breathe']
breathe_projects = {'monprojet': '../xml'}
breathe_default_project = 'monprojet'

Puis, dans index.rst, on demande à Sphinx de faire le rendu de la documentation :

// in doc/source/index.rst
.. doxygenclass:: Cat
    :members:

Si vous appréciez le thème de ReadTheDocs, vous pouvez l'installer :

pip install sphinx_rtd_theme

et l'utiliser :

# in doc/source/conf.py
html_theme = 'sphinx_rtd_theme'

Maintenant que tout est mis en place, il est temps de générer la documentation.

Générer la doc

Dans la racine du projet, on commence par lancer doxygen :

doxygen Doxyfile

Puis on lance sphinx :

cd doc
make html

La documentation est ensuite disponible dans le dossierdoc/​build/​html.

Voici ce que j'obtiens :

Intégration et Déploiement continus avec GitLab CI/​CD

Lancer ces deux petites commandes, c'est sympa mais, si c'est à faire à chaque nouvelle version du code source, cela peut vite devenir lourd …

C'est là qu'interviennent les pipelines CI/​CD (Continuous Integration /​ Continuous Deployment) de GitLab : vous faites un push de vos modifications de codes sources et cela met à jour votre documentation en conséquence. Génial, non ?

Voici un exemple de .gitlab-ci.yml

image: python:3.10-alpine

docs:
  script:
  - apk update && apk add doxygen make
  - doxygen Doxyfile
  - pip install sphinx
  - pip install sphinx_rtd_theme
  - pip install breathe
  - cd docs && make html
  artifacts:
    paths:
      - ./docs/build/html
  only:
  - main

pages:
  stage: deploy
  script:
  - cp -r ./docs/build/html/* ./public/
  artifacts:
    paths:
      - public

Ainsi, la règle docs génère la documentation en utilisant la méthode présentée précédemment, et la règle pages déploie la documentation générée sur GitLab Pages (ici sur frama​.io).

Bonus : Générer la documentation avec une règle Cmake

Dans le billet de blog Microsoft, la méthode présentée est intégrée au processus de build de Cmake pour le projet C++ concerné.
Voici donc une solution, si vous souhaitez que la gestion de la documentation se fasse avec le CMakeLists.txt.

Il faut commencer par créer un fichier CMakeLists.txt.
CMake est un système assez complet, et il faudrait une article dédié pour le présenter plus en détail (avis aux amateurs 😉 ), mais je vais reprendre ici un exemple simpliste de configuration, adapté à notre chère librairie féline…

cmake_minimum_required(VERSION 3.14)
project(autodoc)

set(CMAKE_CXX_STANDARD 17)

file(GLOB_RECURSE HEADERS "include/*.hpp")
file(GLOB_RECURSE SOURCES "include/*.cpp")

set(EXECUTABLE_NAME "autodoc")

add_executable(${EXECUTABLE_NAME} ${HEADERS} ${SOURCES})

install(TARGETS ${EXECUTABLE_NAME} DESTINATION bin)

#
# DOCUMENTATION
#

# Auto documentation
## Doxygen
find_program(DOXYGEN
  NAMES doxygen
  DOC "Path to doxygen executable"
)

## Sphinx
find_program(SPHINX
  NAMES sphinx-build
  DOC "Path to sphinx-build executable"
)

if(DOXYGEN AND SPHINX)
  add_custom_target(
    doc
    COMMAND ${DOXYGEN} ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile
    COMMAND cd docs && make html
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
    COMMENT "Generating API documentation with Doxygen"
    VERBATIM
  )
endif()

La partie qui nous intéresse se situe après le "# DOCUMENTATION". On y retrouve les deux config pour les deux outils que l'on utilise. CMake commence par vérifier que les deux outils sont installés avec find_​program puis on ajoute une règle "doc" au ficher Makefile généré plus tard par CMake avec "add_​custom_​target".

Pour compiler tout ce beau monde, dans un dossier "build":

mkdir build
cd build
cmake ..
make # Compiler le binaire C++ si ça vous chante...
make doc # Compiler la doc avec Doxygen + Sphinx

Et voilà : vous trouverez la sortie html de Sphinx dans même dossier qu'auparavant, que vous pourrez visionner en local, ou téléverser sur le web.

Remerciements

Merci beaucoups aux relecteurs : Jonathan, Sebastien et Yoann.

Références




Commentaires

Laisser un commentaire

Pour insérer du code dans vos commentaires, utilisez les balises <code> et <\code>.