Warning: Cannot modify header information - headers already sent by (output started at /home/gillesbld/www/weblog/inc/config.php:41) in /home/gillesbld/www/weblog/inc/clearbricks/common/lib.http.php on line 222

Warning: Cannot modify header information - headers already sent by (output started at /home/gillesbld/www/weblog/inc/config.php:41) in /home/gillesbld/www/weblog/inc/clearbricks/common/lib.http.php on line 224

Warning: Cannot modify header information - headers already sent by (output started at /home/gillesbld/www/weblog/inc/config.php:41) in /home/gillesbld/www/weblog/inc/public/lib.urlhandlers.php on line 65

Warning: Cannot modify header information - headers already sent by (output started at /home/gillesbld/www/weblog/inc/config.php:41) in /home/gillesbld/www/weblog/inc/clearbricks/common/lib.http.php on line 247
informatique - Embedded weblog - page 2

Embedded weblog

Aller au contenu | Aller au menu | Aller à la recherche

mardi, septembre 1 2009

le serpent et le troll

Comme je le dis toujours à mes étudiants, dans ce métier, de nos jours, il faut savoir tout faire, depuis l'assembleur (mécanique les mains dans le cambouis) jusqu'à l'Ajax (et la cuisine brille !). Voilà comment un ingénieur en informatique embarquée se retrouve à faire de l'IHM pour son backend (qui, je vous rassure chers lecteurs, est de type pilotage de matériel, il ne faut pas pousser non plus).

Une Interface Homme Machine, GUI en anglais, le nom fait peur ; de nos jours, on a plutôt tendance à faire dans le web service, à force de planter des projets de "progiciels" (déjà, quand on s'amuser à changer les noms, c'est qu'il y a du louche). Mais ce qui est demandé par le client, c'est du clickodrôme simple, efficace, qui propose d'exécuter différentes actions. Mon idée : Python + QT. Le résultat : impressionnant. Et en plus (surtout, pour le projet qui m'intéressait) : extrêmement portable (Linux/Windows/Mac/BSD/p'têtre même Solaris, c'est dire).

Prérequis : savoir coder en Python (ce que je prendrai réellement comme prérequis dans cet article : c'est assez facile à appréhender), et connaître la bibliothèque (notamment graphique, mais pas que) Qt de Trolltech, c'est-à-dire à présent Nokia (ce que l'on va étudier ici). Je vous rassure, avant de commencer ce projet, je ne connaissais ni l'un ni l'autre, mais j'en avais un bon a priori. Avec une autoformation rapide (d'abord en Python) et du développement itératif (on dit "méthode agile" pour faire style, de nos jours, mais je préfère toujours les histoires de bazar contre les cathédrales, même en développant seul -- avec mes multiples personnalités, certes, mais elles ne sont pas rémunérées --, ce qui ma foi réussi toujours -- on nomme ceci la "méthode à Gilles"), on y arrive très bien (ce que l'on dit toujours après avoir galéré deux heures à chercher une feature toute bête dans son esprit, et qui finalement marche du feu de dieu) ; merci le Qt Assistant (l'aide complète que l'on peut trouver dans le Qt Designer ou sur le net) et le Python reference manual ; et les (trop) nombreux sites web d'(entre-)aide (bonne chance pour trouver, cependant).

Commencez par installer Python, Qt, et le binding PyQt qui va bien avec vos versions Python/Qt (toute bonne distrib' Linux possède des paquets homogènes, sous windows ce n'est pas bien complexe, pour les autres débrouillez-vous vous l'avez cherché). Juste une précision sur les licences : Python est interpréteur de langage script, il n'y a donc aucun impact ; Qt est passé en LGPL sur ses dernières versions (>=4.5), cela n'a donc pas d'incidence sur votre code (seulement sur les éventuelles modifications que vous apportez à la bibliothèque même) ; en revanche, le binding est sous GPL, ce qui implique donc que votre code le sera aussi. La société privée qui développe le code (et il faut bien qu'ils mangent) propose en revanche une licence commerciale ; le business model est donc similaire à celui de Trolltech (peu) avant son rachat par Nokia (qui se moque à présent de faire de l'argent avec : ils voulaient surtout récupérer leurs technos pour faire... de l'embarqué bien sûr !).

On commence simplement : ouvrir le Qt Designer (Qt Creator maintenant, on n'arrête pas le progrès), et demander de créer une nouvelle fenêtre principale. Hop, la voilà qui s'affiche, on peut à présent la décorer de multiples boutons, label, menus, groupes, et j'en passe, le tout par simple glisser déplacer ; une fenêtre permet aussi de changer les propriétés des éléments graphiques, y compris la fenêtre elle-même (l'aide peut se trouver dans l'assistant). On enregistre : ça crée un fichier en ".ui" (user interface devine-t-on), qui n'est autre que du XML. Il va falloir passer ce fichier à la moulinette pour produire du code non pas C++ mais Python : pour cela, pyuic4 est à disposition (on suppose le fichier en ".ui" enregistré dans le répertoire "ui/").

pyuic4 -o ui_projwindow.py ui/projwindow.ui

Le fichier généré comporte alors une classe du nom de la fenêtre que vous avez créé (propriété Name renseignée dans le designer), par exemple "Ui_ProjWindow" ; celle-ci comporte deux fonctions, "setupUi" qui crée la fenêtre (chaque élément, bouton, label, etc, est créé et ajouté à la fenêtre, et ses propriétés initialisées), et "retranslateUi", qui appelée par la première fonction met en place les textes dans la langue que vous avez choisi. D'ailleurs, ce qui est chouette, avec Qt, c'est la localisation, et c'est ainsi que vous n'avez pas à vous soucier de remplacer "yes" par "oui" dans les boîtes de dialogue : il suffit simplement de connaître les bonnes incantations magiques (voir plus bas).

Nous avons donc une classe fenêtre, il va falloir l'appeler pour qu'elle s'affiche. La tradition agit en deux temps : d'une part une petite instanciation générale dans un fichier ".pyw" (l'extension est mal reconnu sous Linux, mais très bien sous windows ; donnez lui le droit de s'exécuter sous Linux, et appelez-le directement), et d'autre part le code massif de l'application en elle-même. Soit "proj.pyw" la première, et projwindow.py la seconde.

Disséquons proj.pyw :

#!/usr/bin/env python
# -*- coding: iso8859-1 -*-

# on importe de quoi créer une application graphique
from PyQt4.QtGui import QApplication
# on importe un minimum de la bilbiotheque (ce qui est dans QtCore n'est pas graphique)
from PyQt4.QtCore import Qt, QLocale, QTranslator, QLibraryInfo, QString
# on importe la classe qui va gerer l'application graphique
from projwindow import ProjWindow

# on entre ici des l'execution de ce fichier
if __name__ == "__main__":

# creation de l'application
    app = QApplication(sys.argv)

# incantations pour traduire la fenetre Qt dans la bonne langue
    locale = QLocale.system().name()
    translator = QTranslator ()
    translator.load(QString("qt_") + locale,
            QLibraryInfo.location(QLibraryInfo.TranslationsPath))
    app.installTranslator(translator)

# creation de l'interface graphique complete
    window = ProjWindow()
# affichage d'icelle (bloquant)
    window.show()
# en attente de fermeture
    sys.exit(app.exec_())

Jusqu'ici, rien de sorcier. On peut s'amuser à créer un hook (un crochet, quoi) qui affiche les exceptions Python dans une boîte de dialogue simple. Pour cela, après la création de l'application, insérer la ligne "sys.excepthook = excepthook", et définir au dessus de notre code la fonction "excepthook" :

import sys, os, traceback, time
from PyQt4.QtGui import QDialog, QLabel, QVBoxLayout

def excepthook(except_type, except_val, tbck):
    """ crochet python pour gerer les exceptions """
# on recupere la pile d'execution
    tb = traceback.format_exception(except_type, except_val, tbck)
# on cree une boite de dialogue avec un bouton de fermeture
    diag = QDialog(None, Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint)
# defintion du titre
    diag.window().setWindowTitle("Une exception est survenue")
# la trace est une liste, on la concatene avec "join" et on la met dans un label
    lab = QLabel(''.join(tb))
# le label gere les retours a la ligne automatiques (evite d'avoir une boite qui fait la largeur de l'ecran)
    lab.setWordWrap(True)
# on donne la possibilite de selectionner le texte affiche avec la souris
    lab.setTextInteractionFlags(Qt.TextSelectableByMouse)
# on cree une boite verticale
    layout = QVBoxLayout()
# on ajoute le widget label cree au dessus au layout
    layout.addWidget(lab)
# on defini le layout par le precedent
    diag.setLayout(layout)
# on affiche la boite de dialogue
    diag.exec_()

On voit déjà avec cet exemple comment on crée "manuellement" une boîte de dialogue simple (sans bouton, juste un label) et comment on l'affiche. Plusieurs concepts apparaissent déjà : codé en C++, Qt est très hiérarchisé, et tout élément graphique hérite de la classe "QWidget". Les widget sont organisés selon des "layout", soit horizontaux soit verticaux, qui peuvent s'imbriquer. Voir à ce propos cette page très complète (en C++ forcément, mais vous êtes au moins bilingue). On remarque que le "exec()" de C++ est devenu "exec_()" en Python, tout simplement pour éviter une fâcheuse ambigüité, il faut ainsi prendre garde à ce genre d'exceptions très rares au niveau du binding. D'ailleurs il faut bien se rendre compte du travail de lien entre C++ et Python, qui peut ne pas être exempt de bugs (une mauvaise utilisation de la bibliothèque, au lieu de remonter une exception, peut très bien faire segfaulter l'application -- cela reste assez rare). Enfin, il faut remercier ce projet de gestion complète de graphes (un vrai bonheur d'inspiration) pour m'avoir donné l'idée de la fonction ci-dessus (qui s'avèrera je suis sûr fort pratique en cas de bug chez le client -- sait-on jamais, ma divinité est grecque et limitée -- pour nous remonter l'information) : je vous invite à aller admirer ce que l'on peut faire dans le genre hyper-poussé en PyQt !

Mais revenons-en à la création de notre interface en elle-même. Dans le fichier projwindow.py, placé dans le même répertoire, on va trouver la définition de la classe ProjWindow, qui hérite l'interface graphique "visible" créée dans le designer, et à laquelle va être "hooké" des fonctions diverses et variées.

# -*- coding: iso8859-1 -*-

import os, sys
from PyQt4.QtCore import pyqtSignature, QString, Qt, QVariant, QRect, QRectF, QThread, QEvent, QSize, SIGNAL, SLOT
from PyQt4.QtGui import *

from ui_projwindow import Ui_ProjWindow

class ProjWindow(QMainWindow, Ui_ProjWindow):
    def __init__(self, parent = None):
    QMainWindow.__init__(self, parent)
    self.setupUi(self)

Comme on voit, l'initialisation de la classe "ProjWindow", qui hérite de "Ui_ProjWindow" va appeler "setupUi", initialisant les graphismes. Mais cela après avoir appelé l'initialisation de QMainWindow, en faisant dès lors la fenêtre principale du projet (car on peut aussi créer les boîtes de dialogue ou les fenêtres filles avec le designer). On remarque au passage que l'application est codée en iso8859-1, ce qui permet de mettre des accents (faire attention lors de l'enregistrement du fichier) ; je ne l'ai pas mis en utf8 pour cause de problèmes avec l'affichage de texte dans widget purement graphiques (j'y reviendrai), mais on note que le générateur Qt-Python crée les fichiers en utf8, permettant donc l'inclusion de tous les caractères de la création. Il faut à présent compléter la fonction d'init de tout ce que l'on souhaite pour l'initialisation de notre application graphique (création de variables locales à l'objet, calculs divers, ouverture de fichiers, etc), et surtout : des crochets pour l'interactivité.

En Qt, l'intéractivité se gère via les signaux. On peut trouver de la bonne littérature sur le sujet, mais il faut déjà veiller à réadapter la syntaxe pour Python. Prenons quelques exemples.

    self.connect(self.menuSave, SIGNAL("triggered()"), self.menu_save)

On connecte ainsi le "signal" nommé "triggered()" de "menuSave" (un item de menu) à la fonction "menu_save(self)" (qui reste à définir plus bas dans la classe). Je rappelle que "self." indique que l'on se réfère à des procédures ou des variables de l'objet lui-même (équivalent de "this" en C++ ou Java, à ceci près qu'en Python c'est obligatoire de préciser). "triggered()" correspond à un clic sur un menu. On peut trouver "clicked()" sur un bouton, "toggled(bool)" pour une boîte de sélection à cocher (indique que l'on vient de cliquer sur l'un des éléments de la boîte), tout comme il y a "valueChanged(int)" sur un slider ou une boîte numérique (la fonction hookée devra alors prendre en argument un entier, qui prendra la valeur de l'objet graphique), ou encore "currentIndexChanged(QString)" pour une boîte de sélection, à différencier de "currentIndexChanged(int)" (le premier renvoie la chaîne de caractère de ce qui a été sélectionné, le second le numéro de l'index ; le traitement est donc différent dans la fonction appelée, sachant cependant que l'on peut ensuite récupérer ses informations en questionnant les propriétés de l'objet).

On peut aussi s'amuser à "automatiser" le traitement de signal, par exemple le fait de changer la combo box "combo_texts" change le texte du label "label_textcombo".

    self.label_textcombo = QLabel(self)
    self.combo_texts = QComboBox(self)
    self.combo_texts.addItem("toto")
    self.combo_texts.addItem("tata")
    self.combo_texts.addItem("titi")
    self.connect(self.combo_texts, SIGNAL("valueChanged(QString)"), self.label_textcombo.setText)

Pour ne pas se casser la tête, et parce que coder fait mal aux doigts (après il faut traiter l'arthrite, ça fait cher pour la SECU), Qt Designer dispose d'une petite fenêtre sympathique "Signal/Slot Editor" (il y a plusieurs autres onglets : "Action Editor" qui permet de gérer les menus, notamment d'ajouter des raccourcis clavier ; et "resource browser", qui doit servir à gérer les fichiers de langue, par exemple, à vue de nez), qui permet de choisir un élément graphique d'émission, un signal (comme on a vu), un autre élément graphique cette fois de réception, et un slot associé (c'est-à-dire une fonction interne comme "setValue(int)", "setText(QString)", etc) ; tout sera généré ensuite automatiquement en Python.

Évidemment, à toute règle ses exceptions : gérer le surlignement à la souris (passage de la souris par dessus un widget, par exemple un label) se fait via une surcharge de fonction :

        self.my_label.enterEvent = self.my_labelEnter

    def my_labelEnter(self, event):

De même lorsque la souris quitte notre label, ou lorsqu'on clique dessus :
        self.my_label.mousePressEvent = self.my_labelSelect
        self.my_label.leaveEvent = self.my_labelLeave

Idem pour la roulette de la souris, par dessus un QGraphicsView (cf plus bas) pour zoomer, par exemple :

       self.mygraph.wheelEvent = self.wheelZoomGraph

    def wheelZoomGraph(self, event):

Pour les événements de redimensionnement de la fenêtre et de fermeture, il suffit donc de surcharger les fonctions :

    def closeEvent(self, event):

    def resizeEvent(self, event):

Noter que l'on peut annuler un événement :

        event.ignore()

Vous en savez à présent assez pour écrire les doigts dans le nez (pardon, sur le clavier) un classique "hello world", et donc recoder avec un peu plus de temps l'ensemble des applications KDE. Il suffit juste de connaître quelques autres trucs et astuces (liste évidemment non exhaustive).

Création d'une boîte de dialogue simple :

    QMessageBox.critical(self, "Erreur !", "Une action a tout cassé.")

La boîte s'affiche toute seule, et attend que l'on clique sur "OK". D'autres boîtes prédéfinies existent, parfois avec plusieurs boutons :

    ret = QMessageBox.question(self, "Question existentielle", "Suis-je buggué ?",
                       QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
    if ret == QMessageBox.No:
        return False

Pour ajouter des boutons personnalisés à une boîte de dialogue standard (on peut aussi ajouter des boutons prédéfinis tel QMessageBox.Abort) :

    msgBox = QMessageBox(QMessageBox.Critical, "Erreur !", "Une action a tout cassé.", QMessageBox.Ok, self)
    quitButton = msgBox.addButton("Quitter urgemment", QMessageBox.ActionRole)
    ret = msgBox.exec_()
    if msgBox.clickedButton() == quitButton:
        sys.exit()

Pour ajouter une option à notre boîte (on s'arrêtera là, on peut tout personnaliser, ça risque de faire long) :

    ch = QCheckBox("Redemander inlassablement", None)
    ch.setCheckState(Qt.Checked)
    msgbox = QMessageBox(QMessageBox.Question, "un tas d'étapes", "Voulez-vous passez à l'étape #2 ?",
                   QMessageBox.Yes | QMessageBox.No, self)
# notez ici l'utilisation du layout pour ajouter le widget
    msgbox.layout().addWidget(ch, 1, 1)
    ret = msgbox.exec_()
    if ret == QMessageBox.No:
        return
    # faire des trucs
    if ch.checkState() == Qt.Checked:
        msgbox.setText("Voulez-vous passer à l'étape #3 ?")
        ret = msgbox.exec_()
        if ret == QMessageBox.No:
            return
    #etc

Il existe aussi des boîtes de dialogue prédéfinies pour diverses actions (que l'on devinera aisément) :

    dirname = QFileDialog.getExistingDirectory(self, "Sélectionner un répertoire", default_path)

    file = QFileDialog.getOpenFileName(self, "Ouvrir un fichier", default_path, "Text (*.txt *.odt)")

    file = QFileDialog.getSaveFileName(self, "Enregistrer", "monbidule", "*.txt")
    if not file:
        return
    if not file.endsWith(".txt"):
        file += ".txt"

Autre fonctionalité sympathique et un peu cachée, l'affichage de texte dans la bande du bas de la fenêtre principale ("status bar") ; ici, durant 4 secondes (optionnel, mais penser alors à effacer manuellement en définissant un texte valant "") :

    self.statusBar().showMessage("la vie est belle", 4000)

Pour afficher/masquer et rendre disponible/indisponible un objet graphique, on procède avec :

   # griser
    self.mything1.setEnabled(False)
    # cacher
    self.mything2.setVisible(False)

Pour changer un texte de couleur et de propriété (c'est un peu lourd, mais extrêmement flexible ensuite) :

    self.color_text_black = QPalette()
    self.color_text_black.setColor(QPalette.WindowText, QColor(0, 0, 0))
    self.color_text_green = QPalette()
    self.color_text_green.setColor(QPalette.WindowText, QColor(65, 155, 50))
    self.color_text_red = QPalette()
    self.color_text_red.setColor(QPalette.WindowText, QColor(185, 50, 20))
    self.fontItBold = QFont()
    self.fontItBold.setItalic(True)
    self.fontItBold.setBold(True)
    self.fontDefault = QFont()
    # vert !
    label.setPalette(self.color_text_green)
    # italique
    label.setFont(self.fontItBold)
    # retour au noir
    label.setPalette(self.color_text_black)
    # font normale
    label.setFont(self.fontDefault)

Il reste l'épineuse question des threads à l'intérieur de l'application, notamment pour les traitements lourds : il est toujours pénible de voir la fenêtre se geler durant un temps indéfini. La méthode peut consister en l'affichage d'une boîte de progression, les exemples sur internet ne manquent pas. Pour cela, Qt met à disposition QThread. Voici comment procéder :

    self.mythread = QThread()
    self.mythread.run = self.myaction
    self.mythread.start()

La fonction myaction sera alors appelée dans un thread. Attention : "mythread" doit bien être une variable de l'objet (ici "WindowProj"), et donc initialisée à "None" avant "__init__", sous peine de voir le thread victime du garbage collector dès la sortie de la fonction appelante ! Une fois dans myaction, on peut faire ce que l'on veut (enregistrer un gros fichier par exemple), sauf interagir directement avec les graphismes. C'est-à-dire qu'il n'est pas possible d'afficher directement une boîte de dialogue "enregistrement terminé", par exemple (ou de le mettre dans la status bar, histoire d'être plus poli). Pour ce faire, il faut passer par des événements personnalisés.

    def myaction(self):
# creation d'un evenement personnalise
        event = QEvent(QEvent.User)
        event.event_type = "my_event"
        # faire des choses interessantes
        if success == False:
# on a detecte que ca a foire
            event.event_action = "failure"
        else:
# on est trop doue (penser a demander une augmentation)
            event.event_action = "success"
# on "poste" notre evenement
        qApp.postEvent(self, event)

# cette fonction est une surcharge pour traiter des event persos
    def customEvent(self, event):
# on filtre
        if event.event_type == "my_event"
            if event.event_action == "success":
                self.statusBar().showMessage("enregistrement réussi", 5000)
            elif event.event_action == "failure":
                QMessageBox.warning(self, "Erreur !", "enregistrement echoue, retenter ?", QMessageBox.Yes | QMessageBox.Abort)


Notons qu'avec Python, on peut ajouter n'importe quel champ simplement à customEvent, sans avoir à le faire hériter (attention à ne pas faire ensuite référence à une variable non existente dans le traitement !). Mais disons que dans l'ensemble, c'est un peu lourd, et que l'on a parfois des résultats pas très chrétiens. Personnellement, je préfère, lorsque l'action peut être découpé en petits morceaux (par exemple pour un enregistrement de fichier dans OpenOffice.org, avec le binding PyUno, on a une ou plusieurs boucles), placer à intervale régulier un :

    QApplication.processEvents()

Ce qui va forcer le traitement des événements en attente sur la fenêtre, et notamment la redessiner. On peut ainsi continuer de cliquer, et tout s'exécutera dans les threads "automatiques" de traitement habituels ; attention cependant à ne pas se prendre les pieds dans le tapis (par exemple, modifier les données que l'on est en train d'enregistrer, ou relancer cette commander : bref, il faut penser à verrouiller ce qui doit l'être, sinon gare au crash !).

Python étant un langage objet, on dispose des mêmes possibilités qu'en C++, et notamment on peut redéfinir certaines fonctions. Citons "resizeEvent(self, event)" et "closeEvent(self, event)" qui si elles sont redéfinies dans notre "ProjWindow" vont permettre d'intercepter les événements de redimensionnement et de fermeture de la fenêtre principale (et donc de l'application pour la fermeture), et pourquoi pas de les annuler avec "event.ignore()".

Gérons un peu de graphisme. Le widget qui sert à afficher est QGraphicsView :

    self.mygraph = QGraphicsView(self)

Il s'agit alors de lui ajouter une "scène", dans laquelle on peut dessiner. On peut ainsi définir plusieurs scènes, et les dessiner hors écran, et les interchanger à l'affichage.

    self.scene_img = QGraphicsScene(self)
    img = QPixmap("graphics/img.jpg")
    self.scene_img.clear()
    self.scene_img.addItem(QGraphicsPixmapItem(img))
    self.mygraph.setScene(self.scene_img)

On peut ainsi ouvrir quasiment tous les formats de fichiers d'image de la création, on peut coller du texte formaté en html, et j'en passe (par exemple, on peut récupérer l'image d'un widget non affiché grâce à "QPixmap.grabWidget(my_widget)", ou encore faire une capture d'écran). Le problème principal est de ne pas s'emmêler entre les QPixmap, les QImage, les QGraphicsPixmapItem -- et ce n'est vraiment pas une mince affaire. Une fois que l'on commence à devenir un Jedi, on peut commencer à ajouter du texte, à zoomer avec la roulette, puis à zoomer automatiquement pour ajuster la vue, et jouer avec la matrice pour ajouter des effets de rotation, etc (il y a des fonctions toutes faites, mais pour savoir comment les utiliser, ça doublerait presque la taille de ce billet -- et puis, je ne vais pas non plus tout vous dire, comment on fait tourner la boîte ensuite, hein, je vous le demande ?).

Il ne reste plus qu'à imprimer !

    def export_to_printer(self):
# creation de l'imprimante
        printer = QPrinter(QPrinter.HighResolution)
# on est en A4 et en couleur
        printer.setPageSize(QPrinter.A4)
        printer.setColorMode(QPrinter.Color)
# différents champs
        printer.setCreator("Dieu")
        printer.setDocName("ma belle image")
# gestion de l'export en PDF (chouette non ?)
        printer.setOutputFileName("image.pdf")

# affichage de la boite de dialogue (automatique)
        dialog = QPrintDialog(printer, self)
        ret = dialog.exec_()
        if ret != QDialog.Accepted:
            return

# creation d'un widget de dessin
        painter = QPainter()
# optionnel : l'antialiasing
#        painter.setRenderHint(QPainter.Antialiasing, False)
#        painter.setRenderHint(QPainter.TextAntialiasing, False)
        self.export_to_pages(painter, printer)

    def export_to_pages(self, painter, printer):
        """ export_to_pages: export the scene on painter with a number of pages
        """
# calcul du nombre de page : arrondi superieur de la hauteur de la scene sur celle du papier A4
        w = printer.pageRect().width()
        h = printer.pageRect().height()
        numPage = int(round(self.scene_img.height() / h))
# on initialise la zone dans laquelle tout ce qui passera par "render" sera sorti sur le QPrinter
        painter.begin(printer)
# boucle d'impression
        for page in range(0, numPage):
# capture de la partie A4 de la scene qui va bien, pour l'envoyer sur papier (avec la meme taille)
            self.scene_img.render(painter, QRectF(0, 0, w, h), QRectF(0, h * page, w, h))
# si l'on cherche a rajouter du texte supplementaire, comme un titre
#           painter.drawText(20, 16, "toto")
# demande de nouvelle page
           if page < numPage - 1:
               printer.newPage()
# fin du rendering
        painter.end()

Voilà c'est tout. On remarque que j'ai sous-traité le "rendering" dans une sous-fonction "export_to_pages" : il s'agit en fait de factoriser l'export en PDF. En effet, en Qt, on peut générer aussi facilement du PDF que ce que l'on imprime. Pour ce faire :

    def export_to_pdf(self):
# boite de dialogue habituelle
        file = QFileDialog.getSaveFileName(self, "Exporter en PDF", "mon_pdf", "*.pdf")
# le printer et le painter
        pdfPrinter = QPrinter()
        pdfPainter = QPainter()
# on veut sortir en PDF !
        pdfPrinter.setOutputFormat(QPrinter.PdfFormat)
# si l'on veut changer la taille du papier manuellement
#        pdfPrinter.setPaperSize(QSize(420, 420), QPrinter.Point)
# mais nous on veut du A4
        pdfPrinter.setPaperSize(QPrinter.A4)
# pour rogner les marges (pas très joli, et l'imprimante risque de vous en vouloir)
#        pdfPrinter.setFullPage(True)
# diverses proprietes (consultable dans votre lecteur PDF favori)
        pdfPrinter.setCreator("Dieu")
        pdfPrinter.setDocName("mon joli PDF")
# definition du nom de fichier de sortie
        if not file.endsWith(".pdf"):
        file += ".pdf"
        pdfPrinter.setOutputFileName(file)
# et l'export tout pareil qu'avant
        self.export_to_pages(pdfPainter, pdfPrinter)

Simple et efficace -- quand on sait comment manier la bête, mais maintenant, vous savez. D'ailleurs, il ne me reste plus grand chose d'important à vous apprendre, chers lecteurs. Allez, peut-être, en bonus : faire un logo de démarrage.

# le texte (a readapter, merci)
splashcopyr='''<font color="black"><b>My Appli v0.2beta<br></b>
Copyright (C) Linagora<br>
Conçu par LINAGORA (Gilles Blanc, gblanc@linagora.com)</font>
'''

def makeSplashLogo():
    """Make a splash screen logo."""
    border = 16
# la taille de l'image a l'ecran, attention au ratio
    xw, yw = 473, 427

# ouverture de "./graphics/logo.png"
    pix = QPixmap(os.path.realpath(os.path.dirname(__file__)) + "/" + 'graphics/logo.png')
# on dessine l'image
    p = QPainter(pix)

# creation d'un document texte
    doc = QTextDocument()
    doc.setPageSize(QSizeF(pix.width(), pix.height()))
    f = qApp.font()
# on ecrit en petit
    f.setPointSize(8)
    doc.setDefaultFont(f)
# on ecrit centre
    doc.setDefaultTextOption(QTextOption(Qt.AlignCenter))
# et paf, on interprete du html en deux coups de cuillere a pot !
    doc.setHtml(splashcopyr)
# translation en bas de l'ecran du texte
    p.translate(0, pix.height() - 80)
# on dessine le texte
    doc.drawContents(p)
# fin du dessin
    p.end()
    return pix

if __name__ == "__main__":

    app = QApplication(sys.argv)
    sys.excepthook = excepthook

# creation du splash screen
    splash = QSplashScreen(makeSplashLogo())
# on affiche le splash screen
    splash.show()
    app.processEvents()

    from tdsdgwindow import TdsDgWindow
#etc (ou retour a la case depart)

Ceci dit, c'est vraiment du cosmétique : les applications Python-Qt s'affichent très vite ! Grosso modo, avec un total de plus de 3000 lignes Python, dont près de 900 générés pour la création du graphisme de la fenêtre, mon application (enfin, la plus grosse) met moins de trois secondes pour s'afficher ! (et bouffer au passage 35Mo de mémoire, environ) En revanche, pour une exécution qui nécessite une recompilation en bytecode du Python (création des fichiers ".pyc" associés aux différents ".py"), il faut deux fois plus de temps. Ce qui normalement, lorsque l'on fait une livraison, n'arrive qu'une seule fois tout au plus.

J'allais oublier un problème épineux (j'avais bien dit pourtant au début que j'y reviendrai, souvenez-vous...) : l'encoding -- on a un nom en French pour ça ? Heu... --, l'encodage de caractères. De nos temps modernes, l'usage de l'utf8 est devenu très courant (tout ça parce qu'il y a des types sur Terre qui ne parlent pas anglais, j'vous jure... Bref). Or, on l'a vu, le fichier projwindow.py est en ascii tout simplement parce que sinon, il se passe des drames dans l'affichage de texte sur les QGraphicsView (vous savez les caractères étranges). Du coup, pour insérer une date en français avec accent (à tester en août, ni en juillet ni en septembre), il faut :

painter.drawText(42, 42, time.strftime("%A %d %B %Y, %H:%M").decode('utf-8')

Pour insérer à présent un texte dans une scène avec "addText", en allant le récupérer depuis un texte de QLabel, on transforme la QString en objet Python "unicode" (ie une "str" en utf-8) :

    caption = unicode(my_label.text())
    text = self.scene_img.addText(caption)
    text.setPos(42, 42)

Voilà, je crois avoir fait le tour de ce qui est le plus important à connaître (je vous ai passé "comment faire un zoom sur une zone graphique avec la roulette", ne m'en veuillez pas, il faut que je garde un peu de savoir-faire sinon je risque le chômage).

Une fois que vous serez passé maître jedi, vous pourrez alors recoder elasticnodes.py, du répertoire "examples/graphicsview" dans les sources du binding, totalement impressionnant. Mais il faudra peut-être lutter un peu, et parcourir un long chemin (petit scarabée), alors sait-on jamais que vous ayez un DIF de côté, contactez-moi donc, je suis sûr que l'on pourra s'arranger...  ;)



Best of webographique :
* sur le site de Python, les bindings de GUI et PyQt en particulier
* des slides de présentation
* intro par la création d'une appli
* intro par l'exemple
* un tuto très complet et très bien fait

mercredi, mai 27 2009

au détour d'une macro crasseuse

#define ATOI2(ar)       ((ar) += 2, ((ar)[-2] - '0') * 10 + ((ar)[-1] - '0'))

Ça se trouve au milieu de date.c, de l'utilitaire date d'OpenBSD. Ça transforme tout nombre entre 10 et 99 (ou 00 et 99) en chaîne de caractère en integer (ou au moins en char), et ça passe même sur un gcc très récent. Outre l'idée d'avancer de deux dans le pointeur puis de référencer en négatif, et de flinguer au passage la variable d'entrée, autre chose interpelle : la syntaxe.

int i = 42;
int t = (2, i++, 4 + i);
printf("%d ", t);

Ceci affiche 47 !! Page 210 de votre K&R (A7.18), opérateur "," :

f(a, (t=3, t+2), c)
a trois arguements et le deuxième d'entre eux vaut 5.

Toutes les personnes interrogées, développeurs expérimentés en C, ne connaissaient pas ; je me suis senti moins seul...

mercredi, avril 15 2009

no comment

Vista today, post-Service Pack 2, which is now in the marketplace, is the safest, most reliable OS we've ever built. It's also the most secure OS on the planet, including Linux and open source and Apple Leopard. It's the safest and most secure OS on the planet today.

Remarks by Kevin Turner, chief operating officer for Microsoft, about the role of CIOs in a changing economy
MidMarket CIO Summit
Redmond, Wash.
April 6, 2009

lundi, mars 23 2009

un troll peut cacher bien des vérités

Cela faisait longtemps que je n'avais point trouvé sujet à troll, et même si ce n'est pas vendredi, voici un billet sur une pile d'appel Java pour le moins... impressionnante. Même en ruby, on ne fait pas mieux, même si c'est déjà beaucoup. D'autant que nous ne somme pas encore dans le code de l'interpréteur. Cependant, il faut paraît-il minorer pour le Java : la pile présentée est celle "virtuelle" (correspondant aux sources), et non la "réelle" qui peut être bien plus optimisée ; on n'en demeure pas moins songeur.

Faut-il crier haro sur le Java pour autant ? (je rappelle que nous sommes sur un blog embarqué) Un ingénieur très expérimenté en informatique industrielle/embarquée (ça tombe bien) dit que non. Et il détaille très longuement. J'aurais tendance à appuyer cet argumentaire, mais constate que l'Ada est tout de même plus sympathique que le Java (même si les effets de bords y sont bien plus aisés, par exemple parser des chaînes de caractères en Ada est à la limite de la calamité). En attendant, chacun sait mon aversion pour le C (d'autant que je le maîtrise parfaitement, sinon je ne pourrais en faire état :)  ). De même, des implémentations "solidifiées" de Java se comptent au nombre de deux (au moins, il faut voir où en est Sun aussi) depuis bien des années maintenant, et je n'ai pas l'impression qu'elles suscitent un grand engoument ; on vérifiera cela sur le stand d'Aonix (qui n'hésite pas à comparer Ada et Java) au prochain salon RTS.

Vers la fin de cet argumentaire très intéresant que j'ai cité, on trouve ceci :

One of the biggest lessons I've learned in my many years of working, interviewing, and managing projects is that specific technical skills are far less important than outlook and character. Technical skills are the easiest skills to teach. They're also the skills that must be updated and replaced regularly.

Le hasard voulait que je tienne précisément le même discours fort récemment, ce week-end pour être précis. Le problème est alors l'enseignement de cette partie très importante du métier de l'ingénieur (à tel point que l'évolution de carrière tend à en faire l'unique sujet, alors que la technique "pure" disparaît du champ d'action à plus ou moins court terme) : pour moi, cette formation de l'esprit passe par du retour d'expériences à travers de la veille technologique et de l'étude de marché (mêler des cas réels à des graphes d'études), par exemple. Simplement, ça ennuie toujours les étudiants...  :)

jeudi, février 12 2009

la consternation ksh

Mais quel est ce comportement ?

There is one restriction on integer variables. Once a variable is set to integer type, it can't be assigned a non-integer value:

$ typeset —i NUM=abc 
/bin/ksh: NUM: bad number

Je ne sais pas bien si c'était à la base une idée de typer a posteriori des variables non-typées a priori, mais dans tous les cas ça pue déjà parce que pas standard comme comportement, et ensuite c'est loin d'être la meilleure idée du siècle dans l'absolu (si le langage est pourri, autant y aller jusqu'au bout !). Mais le pire du pire, c'est que l'erreur renvoyée est aussi parlante que :

script[167]: 08: bad number `08'

Hors, 167, ici, c'est un numéro de ligne qui correspond à la fin du "if" dans lequel on se trouve (if qui fait bien 40 lignes...). Tout à l'heure, j'ai vu la même erreur avec "09", ça arrive une fois toutes les deux heures en test (pour une exécution toutes les 30 secondes), sachant que ça pourrait effectivement bien être dû à une arithmétique de dates. Quelques recherches sur internet montrent que bien du monde s'est arraché les cheveux dessus. Heureusement, la solution était au milieu de tout ça...

Comme d'habitude, on retiendra qu'il faut fuir ksh. Mais décidément, les systèmes UNIX [pré-]historiques en raffolent toujours.

Edit: raté ! C'était :

        case ET_BADLIT:
                warningf(true, "%s: bad number `%s'", es->expression, str);
                break;

dans ksh/expr.c (on remarque les backquotes dans l'erreur). Mais ça ne m'avance guère plus, à vrai dire... (il semble que ce soit bien dans une assignation de variable, en tout cas)

Ré-édit:

Osanna pour Patrick ! (qui a déjà dû subir la même erreur en son temps)  Ah bien c'est que "0" suivi d'un chiffre, c'est considéré comme de l'octal, et justement :

struct tbl *
setint_v(struct tbl *vq, struct tbl *vp, bool arith)
{
        int base;
        long num;

        if ((base = getint(vp, &num, arith)) == -1)
                return NULL;

Sauf que j'avais pas capté (ça m'apprendra à tester avec $((04)) pour une erreur de "08" ou "09" !). Alors, la solution en bois sera de concaténer un "1" au début de la chaîne, et d'enlever 100 ensuite dans l'opération : berk !!

Oh, et ksh est quand même bien pourri dans ses messages d'erreur :

# echo $((4+08))
ksh: 4+08: bad number `08'
# echo $((08))
ksh: 08: bad number `08'
# HOUR=12
# MIN=03
# SEC=08
# TIME=$((HOUR*3600+MIN*60+SEC))
ksh: 08: bad number `08'
# SEC=03
# TIME=$((HOUR*3600+MIN*60+SEC))
# echo $TIME
43383

jeudi, décembre 18 2008

conférence Paris8 (mais cette fois, ce n'était pas moi)

Non, c'était Pierre Gronlier, un plus-que-fort bon ami de mes années d'étude à l'EPITA, qui après avoir brillamment obtenu son diplôme spécialisé dans le logiciel embarqué et temps-réel (je précise, parce qu'on était classé au même niveau, donc je m'envoie indirectement des fleurs), a continué très originalement sur un master à l'ENS Cachan. Spécialité : le traitement de l'image. Tout un programme !

Coopté lors de ma propre conférence auprès de la gente Isis Truck -- qu'il est toujours un plaisir de revoir --, je peux me targuer d'avoir aussi recommandé Basile Starynkevitch pour le 26 janvier prochain, comme quoi je devrais penser à me reconvertir en agence (ça dépendra de mon EAD, hum :D ) ; en revanche, je ne suis pour rien dans la programmation de Loïc Dachary du 2 février suivant, mais le monde est décidément petit. Ce qui est certain, c'est que pour une première expérience, ce fut fort bon. La conférence a porté sur le codage vidéo (et non l'encodage, pensons aux apparatchik de l'orthographe françoise, même si la subtilité est tellement subtile que...), car Pierre travaille pour Actimagine. Le lien avec l'embarqué est évident : le but premier est de permettre une décompression vidéo de leur propre format, Mobiclip, équivalent en qualité au h264, sur des appareils déjà en circulation et non prévus pour (CPU pas assez puissant et GPU absent), ou disposant de peu de batterie : téléphones, consoles vidéos, etc. Bon, c'est pas libre, mais franchement, la démonstration est assez bluffante pour ne pas dire que ça pue, à moins d'être malhonnête (et puis, il y a aussi des choses pas libres qui puaient, et qui une fois libérés sentent toujours fort, Java par exemple, et le reconnaître vous empêche d'être qualifié d'ayatollah du libre, alors...). En revanche, aussi excellent soit le produit, il serait bien de penser à une version Linux autre que celle de l'ami Pierre sur son PC. Bref...

Pierre a parlé d'à peu près tout, et de fait ça a duré : après avoir galéré sur la ligne 13 et être arrivé à 18h20 (18h00 en prévision, c'est tôt, mais bon...), la présentation a terminé bien après 20h00 (une bouteille entière d'une célèbre boisson désaltérante fut sacrifiée). Différences de format de câbles, d'affichage, de codecs, subtilités afférentes au codage vidéo -- le gamma et ses histoires ubuesques, entre autres héritages-boulets que l'on traîne toujours --, on aura même parlé de la perception visuelle avec une plongée dans notre capteur oculaire portatif (qui peut cependant aussi servir à la drague). Comme les slides sont disponibles, il est inutile de s'étendre.

Mais louons tout de même cette forte introduction-et-plus à ce sujet plus passionnant que ça en a l'air de prime abord (quoique les cours de traitement de l'image n'étaient pas mes préférés, j'ai toujours préféré les manchots en boîte), et surtout que l'on peut être amené à croiser un jour ou l'autre : outre la petite digression dans l'échec de la DVB-H en France (et il se trouve que j'ai bossé sur l'implantation de la DVB-SH : mais en l'occurrence, le simulateur de communication satellite avec le récepteur-retransmetteur terrestre), la partie sur la transmission télévisuelle m'aura rappelé mon passage dans les services de Philips l'année dernière (aujourd'hui racheté par Pace), où j'ai dû ingurgité dans le métro les bases du métier, avec le très bon ouvrage maison (on y parle beaucoup d'OpenTV, de fait, qui comme l'indique son nom est très fermé -- c'est ce qui fait tourner C+, par exemple). Alors il est toujours bon, pour sa culture, de savoir de quoi l'on parle. Surtout lorsque les STB, les télés, les lecteurs DVD et j'en passe migrent tous peu à peu sous Linux (et même les retransmetteurs de DVB-SH, donc...).

J'invite donc tout un chacun à découvrir les 10Mo de slides de Pierre, que je peux encore remercier au passage.

mercredi, décembre 10 2008

les comportements erratiques de syslog-ng

* un fichier sans ligne "destination remote" implique une impossibilité de démarrer syslog-ng (avec une erreur franchement pas belle, ça râle)
* un fichier avec un "destination remote { };" garde les logs uniquement en local
* un kill -HUP envoyé sur un syslog-ng relit le fichier de conf et le prend en compte (sans changer le PID, mais un changement de filtre en plus permissif montre bien que ça été pris en compte)

Soit un fichier avec un "destination remote" avec ou sans IP à l'intérieur permet de démarrer le syslog-ng sans problème. On enlève la ligne "destination remote". On envoie un -HUP. Question : que se passe-t-il ? Eh bien rien du tout. Encore un bug non détecté... (pour le cas où l'on démarre le démon à la main, ce qui en l'occurrence n'arrivait qu'au démarrage, et pour un fichier sans adresse IP de serveur distant : il fallait deviner que ça ne voulait pas démarrer sans !)

jeudi, décembre 4 2008

quid des lutins ?

La QOTD :

libcurl is completely agnostic about the underlying transport layer being
ethernet, gprs or carrier pigeons!

(Daniel Stenberg, libcurl development)

comment inline changea le monde

Enfin, je pense. Inline, ce n'est pas dans le C, à la base, ai-je appris ce matin ; manifestement, ça n'a été standardisé qu'en 99, alors que GCC le gérait déjà depuis belle lurette (j'ai déjà dit à quel point gcc était génial ? Ah, oui...). Alors comment faisait-on avant ?

Eh bien manifestement, c'est horrible, mais on n'utilisait que les macros. Retenez votre respiration, cher lecteur (de plus de 16 ans, en dessous, fermez votre navigateur immédiatement !), et allez voir par ici...

mardi, novembre 25 2008

ARM European Technical Conference

J'ai pu assister toute la journée de jeudi 20 novembre à la conférence européenne de ARM, qui se tenait à Paris, tout prêt de la tour Eiffel. Le programme était lourd et a priori fort intéressant et les partenaires nombreux et de qualité. Je ne connaissais absolument pas ce rendez-vous de l'informatique embarqué, dont je me demande même s'il a lieu toutes les années en France (j'aurais dû demander, certains étaient manifestement des habitués de l'événement) ; en fait, c'est par le biais d'OK-Labs que j'ai appris l'existence de cette manifestation. La rencontre s'est déroulée depuis le matin, vers 9h00 (je suis arrivé à peine plus tard) et a duré jusqu'à 18h45 environ ; j'ai pu assister à cinq conférences techniques.

A mon arrivée, je me vois offrir, outre un porte-badge plein de poches pratiques, un sac en tissu simple contenant déjà quelques tracts et un programme de la matinée (les sessions sont nombreuses, quatre à chaque fois, et on rappelle sur le badge quelles sont celles pour lesquelles on s'est inscrit), mais surtout, un sac pour PC portable à deux grandes poches, très pratique et de fort bonne qualité : on ne se moque pas du monde, chez ARM ! L'organisation sera parfaite de bout en bout, enfin si j'avais pu savoir où rendre ma feuille de sondage d'opinion à temps j'aurais pu participer au tirage au sort, dommage (de manière fort amusante, je remarque que près de deux goodies sur trois à gagner tourne sur du Linux embarqué !). Parlons donc rapidement, puisqu'on y est, du repas : asiatique ou crustacés (même... des huitres !), j'aurais été subjugué par la fontaine à chocolat : il faut absolument penser à donner une prime à la dame qui a impulsé l'idée !  :)

Je fais un premier tour rapide sur les stands avant d'arriver enfin aux premières conférences communes (outre les cinq techniques, donc) : je n'avais pas imaginé que le nombre de participants serait si grand ! D'ailleurs, comme il y a finalement assez peu de temps libre pour y passer -- trois fois quarante minutes, grosso modo -- je n'ai malheureusement pas pu aller sur tous les stands, dommage. Je commence donc véritablement avec une première intervention de NXP (Geoff Lees, lis-je sur mon programme, je n'ai malheureusement pas eu de slides des interventions -- en fait je me demande même s'il y avait un moyen de les récupérer, peut-être ai-je rêvé --, mais j'ai envoyé un mail au contact ARM pour savoir s'il était possible de les obtenir : on devrait me les envoyer par CD ! :)  ), "The ARM Microcontroller (R)evolution" ; NXP vise à obtenir une rétrocompatibilité binaire parfaite entre ses MCU sur base ARM, je ne sais pas si c'est une si bonne idée (les casseroles du x86 ?), mais en tout cas ça révèle quelque chose que l'on reverra souvent : moins on fait appel au développeur software (même pour une recompilation simple !), et mieux on se porte.

Il me semble d'ailleurs que c'est Semir Hadda, le manager du marketting de STMicroelectronics, qui a traumatisé l'assistance en affirmant qu'il fallait commencer le développement logiciel le plus tôt possible, car c'était là où tout se jouait, à l'heure du hardware peu cher et plus facile qu'avant à obtenir ; penser d'ailleurs à la phase d'optimisation software avant d'augmenter la puissance hardware. Le message est dans la même ligne que celui de Reinhard Keil, le directeur des outils MCU chez ARM, avec sa conférence "ARM Microcontroller: Strategic view". Il ressort de tout cela que les acteurs en ingénierie embarqué sont par exemple intéressé à 70% par des outils de débuggage performants, et ce avant même la considération des performances et de la robustesse des produits : on sent le time to market et la solidité des développements softwares devenus de plus en plus complexes au coeur des attentes -- et des craintes ? -- des différents acteurs.

D'économie de marché, il en a été fortement question avec l'intervention de Malcolm Penn, le CEO de Future Horizons, qui avec "Vision 20:20... Designing Tomorrow's Systems Today", nous a expliqué qu'il a déjà vu des crises, que c'est cyclique, et que l'on est effectivement en train de plonger -- ça coupe avec l'optimisme affiché des précédents intervenants, avec leurs graphiques tous exponentiels !  Il n'y a pourtant pas de quoi s'inquiéter, apparemment : si des gros ont déjà disparu par le passé, cela peut toujours arriver maintenant (j'apprendrai plus tard que Atmel a eu chaud, récemment : pourtant leurs produits sont réputés excellents !), mais le marché survit et se restructure (on dirait presque du Friedman avec une touche de pragmatisme, mais on n'en sort pas bien rassuré tout de même). Il nous entretient ensuite de l'importance capitale, à ce niveau, de la R&D : celui qui ne suit pas meurt, c'est aussi simple que cela, et il faut à l'heure actuelle deux à trois ans pour sortir quelque chose de viable. C'est-à-dire que si une société s'avise de couper dans le budget de l'innovation sur ses MCU, à la nouvelle génération, soit deux à trois ans plus tard (durée qui tend à s'allonger, ce qui représente un problème pour l'arrivée de nouveaux concurrents), cette société disparaîtra brutalement, alors que jusque là tout pouvait aller fort bien. Voilà un avertissement qui a le mérite d'être clair. Cependant il parle très vite (ça part dans tous les sens, d'ailleurs) et ses slides sont plein à ras bord, il saute beaucoup de choses, bref j'espère que l'on pourra retrouver quelque part son analyse et se pencher dessus.

Ainsi s'achève la première session de mini-conférences communes, vers 11h20 ou 11h30. Je peux donc commencer la visite des stands, et en fait le temps passe fort vite en compagnie des intervenants de Virtual Logix ; il faut dire que la paravirtualisation est toujours mon sujet favori, ce qui donne donc lieu à des échanges intéressants (nous parlons notamment d'une fonctionnalité avancée de redistribution à chaud de mémoire vive entre les différentes VM). Qui plus est, le positionnement de Linagora sur le marché peut tout à fait intéresser de manière complémentaire des sociétés proposant du logiciel très embarqué (l'hyperviseur est sous le noyau, avec des impacts au niveau drivers, on est loin du userland !), dont la spécialisation ne peut proposer aux clients souhaitant un système complet (j'ai déjà travaillé sur des projets avec des pages web CGI, par exemple : on peut aller jusqu'à un niveau assez élevé, en embarqué !), ce à quoi Linagora peut tout à fait répondre avec notre équipe d'experts en Linux embarqué. Il est 11h50 : l'heure de rejoindre le rez-de-chaussé (le principal du forum se passe au niveau -1 de CAP15) pour assister à la première conférence technique, menée par Nick Sampay : "Using trace to optimize system performance".

On aurait pu croire à une présentation du debug software en utilisant sondes J-TAG ou autres moyens, mais en fait c'est du hardware dont il va être question, plus exactement de l'organisation des bus et buffers pour dumper registres et pipeline, jusqu'à une éventuelle sortie sur port J-TAG. On est très enfoui, et même si c'est intéressant, un certains nombre de personnes quittent prématurément l'assemblée en se rendant compte que ce n'est pas pour eux ; ce n'est pas vraiment pour moi non plus, mais l'aspect de culture générale n'est pas inutile. Dommage cependant que ce n'étais absolument software, peut-être aurais-je dû aller à "Optimizing Software using ARM tools - profile driven compilation", mais je pense que ça aurait été essentiellement de la publicité pour RVDS, et j'en aurai de toute façon eu pendant encore les deux sessions suivantes...

Durant la pause déjeuner, je rencontre un ingénieur de Trango Virtual Processors, et malédiction, comme un certain nombre d'intervenants, il n'avait pas ou plus de carte de visite, je ne me souviens donc déjà plus de son nom -- nous avons commencé à travailler en même temps pour la société, mais on avait dû se croiser que très rapidement, d'ailleurs j'ai appris que j'avais été le premier "extérieur" à travailler sur Trango, et le premier à expérimenter le développement à distance. C'est l'occasion d'évoquer le rachat récent par VMware : il m'est confirmé que l'entreprise est à présent entièrement la possession du leader américain de la virtualisation (propriétaire), et n'entretient donc plus aucun rapport (déjà très symboliques) avec le groupe Elsys. La société voit cela comme une reconnaissance d'un très grand acteur du marché (c'est plus de 1600 personnes, VMware !) de sa solution, qui rappelons-le est codée à partir de zéro, contrairement aux autres compétiteurs du secteur (VLX et OKLabs se sont basés respectivement sur les microkernels ChorusOS et L4, quant à Sysgo ils ont récupéré leur kernel pour aéronautique PikeOS), lui assurant la plus petite empreinte mémoire (15~20Ko). C'est l'analyse que j'ai aussi faite ici-même.

Je rencontre aussi durant cette pause, et pour la première fois, l'équipe d'OK-Labs. Du moins la fort charmante Brenna Walters, venue tout droit du bureau de Chicago (mais elle est originaire de LA, pas tout le monde n'est australien chez OK-Labs, manifestement !), qui s'occupe justement de la communauté Open Source. Et distribue les T-shirts "I run in privileged mode", phrase d'un humour hautement geekesque... inspirée par elle-même ! Evidemment, nous parlons boutique, puisque mon ancien stagiaire a travaillé durant six mois sur leur produit OKL4, un hyperviseur de paravirtualisation que l'on aura pu tester, comprendre, et pour lequel Linagora aura montré son intérêt et son investissement actif dans la communauté (ML, wiki d'aide, etc). Et puis Abi Nourai, qui prend la direction technique en Europe, à Vélizy (avenue de l'Europe, justement, côté Auchan pour situer), mais qui avait quelques rendez-vous toute la journée : je n'ai donc pu le croiser que fort rapidement à ce moment-là, et ce n'est que plus tard que nous aurons eu la possibilité de faire plus ample connaissance.

En attendant, l'après-midi débutant, c'est à la conférence "Moving to 32 bit MCUs and High Level Programming" que je me suis rendu, la présentation étant toujours assurée par Nick Sampays de ARM.Nous parlons en réalité uniquement de ce que propose Keil pour ce faire. En fait, cette compagnie rachetée en 2005 concentre à présent les outils de développement pour ARM, et on passe donc en revue MDK (Microcontroller Development Kit), avec son compilateur RealView (que je connais fort bien : j'ai eu l'occasion de travailler dessus, pour Trango justement -- et NXP indirectement --, et j'ai entièrement lu les centaines de pages de documentation autant de armcc que de armlink et leurs autres logiciels de la suite), son IDE µVision (qui de base n'a rien de bien original, mais présente des aspects très intéressants : chargement de code sur cible via J-TAG ou simulation du device en deux clics -- avec débuggage facilité --, options de compilations en clickodrome, couverture de code avec statistiques d'exécution, ou encore un analyseur logique qui présente des graphes dans l'idée d'un oscilloscope), sa bibliothèque C avec son pendant très allégé Microlib (on est dans l'idée de la µClibc, mais on annonce 92% de réduction d'utilisation mémoire !), et enfin le câble J-TAG ULINK2. Une grosse page de publicité qui dure 40 minutes, comme toutes les conférences techniques (et non commerciales :)  ).

Problème : il semblerait que au moins µVision (et peut-être les autres logiciels ? Pour RVDS, je sais qu'une version Linux existe, puisque je l'avais) ne soit disponible que sous Windows, en fait on ne parle même pas de l'OS d'accueil, et très généralement tout le monde présente des outils sous Windows dans les stands et les communications (à noter que OKLabs n'utilise que MacOS X, et que Trango est sous Ubuntu). Aussi, Reinhard Keil (mince, de la société Keil, je comprends mieux pourquoi c'est le directeur !) lorsqu'il nous présente la conférence suivante est aussi Windows-centré (on aura même droit à un soft pour éclairer/éteindre des LED d'une carte de dev, compilé en Visual C++... Berk). "ARM Real-Time Library: Facing Embedded Technology Challenges" était alléchant pour la même raison qu'un peu tous les acteurs en R&D embarquée recherchent des outils déjà existants pour réduire le fameux TTM (c'est après les outils de debug l'une des plus importantes attentes). En fait, on va nous parler de RTX, le mini-noyau temps réel de ARM/Keil. Libre de toute royalty, et pesant entre 1,5 et 5ko tout dépend de ce que l'on y met dedans, c'est l'occasion de renouer avec un monde industriel où le hardware est toujours roi : explication de ce que sont UDP et TCP (avec différences), d'Ethernet (avec connexion par PPP, SLIP et dial-up), on évoque aussi les serveurs http avec CGI (quand je disais que c'est à la mode...), le telnet ou le TFTP, le SMTP ou DNS, bref des choses bizarres pour l'électronicien (raté, c'est bien connu), mais tellement standard pour l'ingénieur informatique je pouffe intérieurement de rire lorsque l'on présente cette révolution : "TCP network provides easy solution to connect to the world". Ca remet les idées en place. RTX fournit un file system pour flash (RL-Flash), un support des différentes piles USB par RL-USB (HID/MSD/Audio, l'occasion de présenter rapidement cette grande avanceé qu'est l'USB...), et même un support du bus CAN (RL-CAN, on l'aura deviné). Possibilité d'obtenir les sources dans la version deluxe que l'on imagine plus chère, à l'exception de la pile TCP/IP (toujours la partie sensible...). Je rêve d'une comparaison de tout cela avec RTEMS.

45 minutes de pause, je passe par NXP qui présente sa série LPC (IDE sous Windows) avec un anglais néerlandais qui n'invite pas trop à s'attarder, puis je vois Texas Instrument, et c'est la révélation. Car TI (France, à Villeneuve-Loubet, près de Nice, mais ma première interlocutrice Marie-Claire Desjardins, dénuée de carte de visite, m'a bien dit venir de la banlieue parisienne) n'est pas seulement très intéressé par Linux embarqué -- et sur leur stand, une télé diffuse justement les applications graphiques lancées sur une carte : ce qui les attire, c'est le communautaire ! Aussi peut-on lire en cinq pages un document "TI Linux and Open Source Initiative Backgrounder", et pour bien montrer leur engagement, une carte de développement à base de ARM Cortex-A8 est présentée : cette carte n'est pas banale puisque ses spécifications sont entièrement ouvertes, dans un projet communautaire, pour un mini-prix de 149$. En revanche, pas de port Ethernet et un seul USB OTG (on-the-go, c'est-à-dire qu'il peut tout faire, host, target ou audio), mais en échange une sortie HDMI/DVI et une autre S-Video : c'est que l'on peut faire de la 3D avec ce genre de MCU. En effet, les OMAP font partie des choses fabuleuses que l'on peut rencontrer dans l'embarqué (j'ai travaillé moi-même sur le Sagem MO300e, toute la partie système Linux "distribution maison" vient de moi, sur un OMAP730, d'après la photo de famille) : à présent la série des OMAP35x est considérée comme quatre fois plus puissante que la génération précédente des ARM9 (cadencés jusqu'à 450Mhz, mais chez OMAP, on est passé directement aux ARM11 à 330Mhz), et quand on pense qu'avec les 200Mhz de mon processeur je faisais booter en dix secondes un Linux 2.6.19 virtualisé avec un nucleus, noyau Linux qui démarrait dans sa séquence d'initialisation un serveur http (lighttpd) et un autre ssh (dropbear), on imagine alors les possibilités de ce Cortex ! (il va certainement conquérir le monde) Je repars avec beaucoup de documentation (disponible à mon bureau).

L'échange avec TI fut des plus enrichissants, et partageant des visions communes, j'espère très fortement que nous pourrons travailler ensemble. D'ailleurs, ce type de partenariat entre fournisseur hardware et développeurs software pouvait se rencontrer à peine plus loin, sur le stand d'Antycip, qui ne faisait pas que montrer ses belles cartes de développement professionnelles (prévoir de la place pour en stocker une !), mais faisait aussi la publicité de Montavista, dont on ne distribuait pas seulement des yoyos lumineux (avec un système de dynamo : ça c'est goody !) : la plus célèbre des sociétés en Linux temps-réel est en effet recommandé et présentée sur les stands d'Anticyp comme leur intégrateur de Linux embarqué/RT de préférence, et vice-versa. Leur solution de développement et d'intégration sous Eclipse (qui reste un "must-see" : en quelques clics, on se construit un file system complet !) bénéficie par ailleurs d'une diffusion sur écran plasma géant. Cela me donne l'occasion de parler de stratégies (d'alliances, de partenariats), de perspectives (on me confie qu'en réalité le gros du chiffre d'affaire est effectué par la vente massive à des clients très nombreux de microcontrolleurs de faibles performances, et qu'en fait le nombre d'acteurs sur des secteurs tels celui concernant l'ARM Cortex-A8 est très petit, mais cela est compensé par la diffusion massive en millions d'exemplaires des solutions obtenues, eu égard au type d'applications visées), d'état du marché (certains ont eu très chaud cette année, comme je disais plus haut). Malheureusement, encore un qui n'avait plus de cartes de visite, mais il aura eu la mienne (très... artisanale, c'est vraiment la crise pour tout le monde :)  ). Je me précipite alors, avec un peu de retard, à la conférence sur "Software driven low power optimization for ARM mobile architectures/Multimedia processors power management".

Le premier intervenant est de la société Synopsys (intéressant comme nom !), mais je n'ai pas pu noter son nom (en revanche son prénom est Markus) ; il nous préconise de démarrer le développement logiciel as soon as possible (on continuera tout le temps à parler anglais, même lorsque les intervenants sont francophones : il y a toujours quelques personnes dans la salle qui ne le sont pas, tandis que tout le monde parle anglais ; on y repensera très fort la prochaine fois que l'on médira sur notre système scolaire), ce qui ne va pas sans traumatiser les décideurs hardware, mais c'est ainsi : il nous montre avec une courbe que si l'on démarre tôt, on aura le temps, après avoir ajouter des fonctionnalités demandées qui vont charger la mule (soit le CPU), d'optimiser cette partie software, et ainsi de ne pas avoir ni à augmenter la puissance du hard, ni à perdre en puissance disponible (tant en terme de calcul que de durée de batterie). Je ne peux qu'approuver, et il ajoute même que "Software rules power consumption", voilà qui a le mérite d'être clair. Il vante en outre les mérites de la simulation, et donc du profiling. Marie-Claire Desjardins, de Texas Instrument (Engineer, pas commerciale), nous parle ensuite de "the flexible dynamics of power management in multimedia processors", et nous démontre encore en deux coups de cuiller à pot que TI est très impressionnant dans son domaine. On nous présente le système TI SmartReflex, qui permet de diminuer à la volée le voltage pour diminuer les pertes (dynamic voltage and frequency scaling -- DVFS -- et l'adaptive voltage scaling -- AVS), et dispose de deux systèmes complémentaires : le Dynamic Power Switching (DPS, on arrête ou réduit des parties lorsqu'elles sont inutilisées, avec une détermination à chaud) et le Static Leakage Management (SLM, on met le processeur dans un état de veille prolongée en un clin d'oeil, afin de prolonger pendant plusieurs jours la durée de vie de la batterie : c'est ce que l'on trouve par exemple, à mon avis, sur le N770 dès lors que l'on referme le clapet). Ces technologies avancées sont disponibles sur l'OMAP35x, ce qui prouve que je ne disais pas des bêtises en le qualifiant de merveille de l'embarqué. On trouvera sur le sujet (avec des graphes identiques) des papiers sur powermanagementdesignline ou son équivalent pdf sur ti.com.

Il est alors l'heure de passer à la dernière conférence, sur la paravirtualisation. La première partie est assurée par le fraichement nommé European Technical Director d'OK-Labs Abi Nourai : "design a better feature phone with paravirtualization". Son propos est simple, et c'est celui que je soutiens depuis de années pour ma part : l'augmentation des fonctionalités attendues par les appareils est en constante augmentation, à tel point que l'on en arrive à des OS de type "desktop-like", tels MacOS (iPhone), Windows (Mobile/CE) ou Linux. Et aussi à une attente d'ouverture (possibilité de configurer/rajouter des fonctionalités propres à l'utilisateur), qui ne va pas sans impact au niveau de la conception software (il serait facheux, par exemple, puis-je préciser, qu'un utilisateur lambda de téléphone puisse transformer son appareil en brouilleur). Aussi, nous sommes partis de systèmes d'exploitation simples, et à présent ils sont très sofistiqués. Cependant, les piles de communications représentent de gros investissements, et le re-engineering augmenterait de facto le Time to Market, ce que l'on ne peut jamais se permettre. La solution est alors de faire appel à la paravirtualisation (ou comment ménager la chèvre et le chou dans deux espaces mémoires séparés). OKL4 est alors rapidement présenté -- on évoque rapidement sur les slides le trop peu connu FASS (Fast Adress-Space Switching : je ne trouve pas de lien probant sur le net, c'est toujours une galère sans nom de trouver des informations dessus !), mais on n'ira pas aussi bas dans le discours. Ce qui est certain, c'est que OpenKernel Labs attaque fort niveau stratégie : plus de 200 millions d'appareils (devices) contenant leur solution actuellement distribués, ils tablent sur 250 millions à la fin de l'année, notamment grâce au HTC Dream (G1) qui fait tourner un Linux Android ! Ils ont de plus une "agressive roadmap" (je me mets à parler franglais, c'est pour ma future carrière de commercial) pour la preuve formelle de sûreté de fonctionnement ("correctness", c'est moins fort que la sûreté peut-être, mais comme rien de précis n'a été indiqué... On peut penser à EAL 5 ou 6). OKLabs se définit actuellement comme le leader du marché.

C'est alors au tour de Jean-François Roy de Trango (déjà rencontré sur le salon RTS 08, il est entré après moi chez Trango, je n'ai donc jamais travaillé avec lui, contrairement à Pierre Coulombeau ou Bruno Zoppis, qui s'occupaient avant des salons français, et sont manifestement devenus globe trotters hors France à présent : Trango a d'ailleurs une entité d'une vingtaine de personnes aux USA, soit une bonne partie des salariés de la société !) : si avant la dernière conférence de la journée chacun disait expédier sa présentation en largement moins de 20 minutes, finalement ils mirent chacun une bonne demi-heure. Pourtant, "achieving drivers portability through platform virtualization" (hhmm, driverS ?) ne parla pas technique, du moins on a juste évoqué les possibles répercussions à la fois sur le code -- le plus petit du marché, 20 à 30 Ko -- de Trango ou sur celui du noyau qui doit utiliser la mémoire partagée par ledit driver pour récupérer des piles ou autres, sans énumérer lesquelles. En fait, on a plutôt parlé du pourquoi d'une idée aussi saugrenue (GPL, ABI d'un pilote fermé qui n'est plus en adéquation avec celui d'un nouveau kernel), et de ses mérites logiciels, comme l'indépendance à l'OS ou l'invulnérabilité de celui-ci à un crash éventuel du driver, porté hors noyau (on évitera ainsi un panic/Oops sous Linux). On insiste en tout cas surtout par le rachat récent par VMware, datant du 14 octobre (et rendu public le 10 novembre), d'ailleurs toutes les slides ont migré sur le modèle bleu ciel VMware !

C'est ainsi que s'achève cette journée du ARM European Tecnhnical Conference. Enfin, je fais tout de même un dernier tour des stands, et comme je discute avec un ingénieur d'Adeneo, qui serait notre concurrent direct en terme de développement et intégration spécifique de Linux embarqué (mais dans une optique SSII forfait/prestation à 70%/30%, proche de l'industrie lourde, essentiellement basé à Lyon et un peu à Massy, pour ce que j'ai compris), je rate le tirage au sort. Je termine ma soirée en compagnie d'Abi et Brenna d'OKLabs, qui aurait bien bu une bière avec moi (ou plutôt un lait-fraise pour ma part), mais j'avais malheureusement quelqu'obligation : ce n'est que partie remise ! En tout cas, nous avons fait deux photos que j'espère pouvoir récupérer et publier ici-même. Très, très bon contact avec nos amis d'OKLabs, ce qui confirme la fort bonne impression des précédents entretiens téléphoniques et par mail que nous avions pu avoir ; j'espère que nous pourrons un jour proche travailler sur des projets communs.

En conclusion on peut remercier amplement (ce que j'ai déjà fait là-bas et pas mail) l'équipe organisatrice de ARM, et j'ai pour ma part gardé le ARM bag (mieux qu'un handbag ! :)  ). J'ai été surpris par le très bon format de l'événement, finalement on aurait fort bien pu y emmener notre PDG, mais je ne pouvais pas le savoir avant ; peut-être pourrions-nous y exposer l'année prochaine, les stands concentrés sur le logiciel pur étaient peu nombreux mais tout de même présent, c'est à voir, donc (dans tous les cas, je ne pense pas qu'une exposition à RTS soit souhaitable, par exemple : l'événement aura lieu strictement en même temps que solution Linux, à quelques mètres de là ; en revanche je vais relancer le responsable des conférences). Il est seulement regrettable de n'avoir pas eu assez de temps, avec les conférences, pour faire le tour de tous les stands (comme le montre la dernière photo prise juste à la sortie d'une conférence, les rangées étaient plutôt vides durant celles-ci, alors qu'il était difficile de se déplacer pendant les pauses avec l'assistance très nombreuse). La documentation rapportée a déjà pu être redistribuée à ceux qui ont montré un vif intérêt (celle de TI pour l'instant remporte le plus de succès ; je pourrai en outre mettre à disposition le CD avec les slides dès sa réception), et certains contacts pris (ou repris) mèneront je l'espère à des relations privilégiées. Si l'on ajoute que les conférences et la prise de température générale du secteur ont pu enrichir mon point de vue sur la situation actuelle, je pense que cette journée de visite fut une expérience des plus réussies.

mercredi, novembre 19 2008

GCC everywhere

Hier soir, Basile Starynkevitch (j'arrive à écrire son nom d'une seule traite maintenant ! :)  ) nous a présenté GCC, ou du moins sa partie propre à la compilation (nous n'avons donc pas abordé (ld, objcopy ou gdb, par exemple : nous n'avions qu'une heure trente !), dans le cadre d'une conférence libre pour Parinux (et l'APRIL aussi, si l'on veut). L'organisation, tant sociale que technique du projet, a été au coeur de cette descente dans les entrailles du compilateur libre le plus célèbre. Et ce qui tombe bien, c'est que Basile est passionné par la même chose que nous : l'embarqué !

J'insiste souvent dans mes slides de cours ou de conférence sur le fait que GCC est le pendant sous-estimé de "Linux Embarqué" : non seulement ce logiciel libre sous copyright de la FSF et de facto sous GPLv3 est utilisé dans tous les projets Linux existant (je ne vois pas quel projet ne le ferait pas, on peut supposer que ça existe, mais j'attends de voir...), mais en plus il fait partie intégrante depuis des années des SDK d'OS temps réels bien propriétaires, tel celui de VxWorks ! De fait, GCC n'étant soumis à aucune licence restrictive, on l'utilise partout au moins pour faire des tests. Aussi, si l'on a un compilateur propriétaire sur un projet (RVDS, ObjectAda, Intel Compiler, etc), la licence étant restrictive à un certain nombre d'utilisateurs, on voit les développeurs utiliser GCC en temps de développement, et n'utiliser que le compilateur propriétaire pour le logiciel à réellement embarqué.

Et encore : le compilateur propriétaire est une espèce en voie d'extinction. Qui a entendu récemment parler des compilos de Thomson ou de ST ? Avant, la mode était de créer un CPU et son compilateur associé ; avec le resserrement de l'offre des architectures (certaines puces Thomson ont disparu depuis quelques années, et le CNES fait appel à ses réserves pour équiper ses satellites !), et l'omni-présence de GCC grâce à son organisation permettant d'attaquer toutes sortes de matériels, il n'est plus rentable d'investir dans des compilateurs fermés. Pis encore : il est absolument évident qu'un "méta"-compilateur tel que GCC, capable d'avaler bien des langages, de compiler ou cross-compiler pour bien des architectures, avec une modularité, des options et des extensions de langage à un niveau jamais atteint ne sera jamais plus dépassé. Microsoft est encore le seul à lutter (et pourquoi pas Borland ?), mais il n'y a qu'à compiler du C++ un peu complexe pour se rendre compte que l'on est loin du niveau de GCC.

De fait, les équipes d'Intel ou AMD sont investies dans le projet, à présent. La collaboration de ces entreprises pourtant  concurrentes par nature est même exemplaire. Mais le succès de GCC serait encore bien supérieur à ce que j'imaginais. On m'a parlé de versions certifiées, en DO-178B, que ça, et un membre du public (ingénieur en informatique embarquée dans une grande société concernée) m'a même assuré que du code sur la ligne 13 était compilé par GCC (cependant, est-ce le navigateur ou les freins, ou est-ce l'éclairage clignotant de la loupiotte à l'approche d'une station ? Ce n'est pas pareil ! Dans l'absolu, il y a du code compilé par GCC dans les avions, il y a même du Linux dans les derniers, mais c'est pour du multimédia : quid du navigateur de bord ? Je doute toujours). J'essaierai de faire parler des contacts, mais ce n'est pas facile : la complexité des solutions actuelles, de leur organisation logistique, et le mutisme du milieu industriel, empêchent d'avoir une visibilité fiable et globale des utilisations des logiciels libres en général, et la question du compilateur employé reste définitivement délicate.

J'espère que les slides seront bientôt disponibles, la conférence était très intéressantes, et il semblerait qu'elle peut-être eu plus de succès que la mienne (cependant, je doute un peu, on va dire ex-aequo :)   ). Le sujet était pourtant fortement technique a priori, et les digressions dans les HIR et MIR internes à GCC en ont perdu quelques uns, qui ont tout de même été ravis d'en apprendre autant. Pour ma part, j'étais ravi de retrouver une application de mes vieux cours de compil (on sait qu'à l'EPITA le sujet majeur de première année est la conception d'un compilateur Tiger). A ce propos, j'ai partagé ma consternation avec Basile d'apprendre que les 4 millions de lignes de code de GCC sont écrites en C majoritairement (un peu de C++, ou de langages qui en génère comme celui de notre conférencier), et pas en Caml, ce qui aurait pu au moins diviser le nombre de lignes nécessaires par 100, sans compter la visibilité ; s'il est vrai que l'auto-compilation de GCC par lui-même est l'un des meilleurs tests qui soit, se serait en réalité une question de culture américaine de formation universitaire à la base de ce (non-)choix technique : dommage.

Terminons sur un mot très positif : GDB dans sa prochaine version aura la possibilité d'intégrer des scripts Python, de telle sorte que ce ne sera plus la peine de lutter avec des copies de lignes de commande pour mettre en place des registres, ou de mettre ses breaks à la main avant de les retirer pour les décaler etc (on se rappelle qu'avec un J-TAG, on n'a droit qu'à deux breakpoints/watchpoints hardware !), ou encore d'arrêter le code, dumper les registres et la pile, faire un step, etc, à la main, ce qui est très fastidieux pour le moment. On peut même rêver dès à présent à un équivalent de Time Machine de Greenhills, c'est-à-dire d'avoir la possibilité de jouer un retour en arrière dans l'exécution du code. Et ça, se serait une très grande nouvelle ! (qui ferait économiser 20.000€, hum... Mais il va falloir du temps)

Remercions encore une fois Basile Starynkevitch pour sa fort bonne prestation, et sa bonne humeur naturelle.

VMware rachète Trango-vp

Le choc : j'apprends ce matin via OKLabs que VMware a racheté Trango Virtual Processors ! Depuis le temps que je répète que la paravirtualisation est l'avenir des systèmes embarqués, voilà que cette nouvelle tendrait à me donner clairement raison. J'essaierai de me renseigner sur les raisons et les modalités de cette acquisition dès que possible, mais pour la petite histoire Trango Virtual Processors faisait jusqu'alors partie du groupe Elsys Design (qui ne serait pas à jour ?) ; qui sont mes anciens employeurs, et j'ai fait mes premières armes il y a un peu plus de deux ans sur... Trango. En effet, la société basée à Grenoble est tout à fait française, enfin, un peu moins à présent manifestement. Il sera intéressant de voir l'intéraction d'un géant de la virtualisation desktop et server dans le monde particulier du logiciel embarqué/temps-réel, car le code de Trango n'a certainement pas beaucoup de rapport avec celui de VMware !

jeudi, novembre 6 2008

à quoi sert typedef ?

A éviter ça ?

snmp_sess_add_ex(netsnmp_session * in_session,
                 netsnmp_transport *transport,
                 int (*fpre_parse) (netsnmp_session *, netsnmp_transport *,
                                    void *, int),
                 int (*fparse) (netsnmp_session *, netsnmp_pdu *, u_char *,
                                size_t),
                 int (*fpost_parse) (netsnmp_session *, netsnmp_pdu *,
                                     int),
                 int (*fbuild) (netsnmp_session *, netsnmp_pdu *, u_char *,
                                size_t *),
                 int (*frbuild) (netsnmp_session *, netsnmp_pdu *,
                                 u_char **, size_t *, size_t *),
                 int (*fcheck) (u_char *, size_t),
                 netsnmp_pdu *(*fcreate_pdu) (netsnmp_transport *, void *,
                                              size_t))

(faut téléphoner au Guiness)

mardi, novembre 4 2008

le diable se cache dans passwd

Celui d'OpenBSD, je précise. C'est qu'il m'en a donné à baver, ce sale bestiaux. Sécurité par l'obscurité bonjour, si je ne peux point relater la malheureuse aventure arrivée, je peux cependant noter toutes les astuces diaboliques qui sont contenus dans si peu de code, et ce qui m'amena à m'y confronter. Code que par ailleurs je n'ai toujours pas entièrement compris, et c'est bien la première fois que ça m'arrive, j'ai même déjà dû maîtriser du code indien, c'est dire (et c'est rusé, un Indien). Tout d'abord, le mode de fonctionnement de passwd : cette commande permet de modifier le mot de passe utilisateur. Ou pas tout à fait. En réalité, l'opération se déroule en deux temps : en premier lieu, changement de /etc/passwd, puis de /etc/master.passwd par le programme passwd lui-même ; ensuite, changement des bases de données /etc/pwd.db et /etc/spwd.db par pwd_mkdb. Ce sont ces bases qui sont utilisées par login pour authentifier les utilisateurs. En toute rigueur, si passwd détecte que le mot de passe est local (il gère aussi les bases kerberos et le système YP), pwd_mkdb est lancé, mais même dans la documentation il n'est pas bien clair qu'il ne faille ajouter l'option "-l" :

     -l      Causes the password to be updated only in the local password
             file.  When changing only the local password, pwd_mkdb(8) is used
             to update the password databases.

Il me sembla avoir expérimenté une fois où la base de donnée ne fut pas mise à jour, c'est même cette fois-là que je découvris l'existence de pwd_mkdb (ce n'est pas très naturel, pour le linuxien). Ce dernier a des options d'appel totalement différentes du premier : on doit notamment spécifier quel est le fichier master.passwd à utiliser, et quel répertoire on doit utiliser. Ainsi, on peut reconstruire une base d'utilisateurs locaux depuis un ramdisk, par exemple (dans le makefile, on parle d'exécution depuis... une disquette). Cette précision a son importance : pwd_mkdb est lié de manière statique, en l'occurrence avec la bibliothèque libutil, non celle du système que l'on compile, mais manifestement celle du système sur lequel on compile (la différence est de taille, surtout lorsqu'on développe dedans) ; de plus, lors d'une modification de la bibliothèque libutil de son arbre de développement, une recompilation depuis la racine n'a pas pour conséquence de gérer la dépendance et de recompiler pwd_mkdb (donc la lier avec la bibliothèque modifiée), ce qui peut avoir de fâcheuses conséquences (tout autant que de sérieuses prises de tête lors d'un développement intensif).

Il faut dire que libutil concentre beaucoup de choses hétérogènes, dont passwd.o, hérité du fichier C du même nom. Dedans se cachent des fonctions que l'on suppose communes à passwd autant qu'à pwd_mkdb ; le premier est ainsi lié dynamiquement avec la bibliothèque -- et en utilise presque toutes ses fonctions (il me semble bien qu'au moins une n'est utilisée par personne). Mais l'on aurait tort de penser cela : la fonction pw_mkdb (remarquez qu'il manque le "d", assez pour devenir fou lorsque l'on fait des recherches) contenue dans cette bibliothèque libutil est ainsi responsable du fork qui appelle pwd_mkdb. A vrai dire, peu de fonctions sont réellement partagées, et elles ne font guère plus de quelques lignes.

Si pwd_mkdb a été prévu dès l'origine, avec son option "-d" pour pouvoir travailler dans des répertoires différents de /etc (ce qui est fort pratique en embarqué pour générer une base à partir des fichiers de mots de passe texte construits à la main, la base étant en binaire), cela est presque de même pour passwd. "Presque" car voilà : il existe une fonction de changement des répertoires à chaud, utilisée par pwd_mkdb, qui fait que lorsque l'on cherche master.passwd dans /toto, on part de la macro /etc/master.passwd pour arriver à /toto/master.passwd. Ces macros sont nombreuses : certaines sont en dur, d'autres dans un fichier d'include ; là encore, problème du build d'OpenBSD : les includes considérés ne sont pas ceux de l'arbre de développement, mais ceux du système d'accueil de la compilation. Autre problème : passwd fait appel dans son code assez souvent aux fonctions de relocalisation, mais il n'y a pas d'option existante comme sur pwd_mkdb : ceci est fâcheux, il faut donc entrer dans le code, et se battre avec des valeurs en dur, qui ont tendance à changer lors de la compilation (réécriture des répertoires rapidement exposés juste avant : il en existe plusieurs mécanismes).

Tout ceci est déjà bien complexe à gérer, mais c'était sans compter sur l'intervention d'un dernier farceur : ptmp. De base, ce fichier est prévu pour être dans /etc. Mais il peut être relocalisé en fonction du code de passwd, ou plutôt celui de la libutil, et ce à chaud, contrairement à ce que laisser penser sa macro (qui est bien "/etc/ptmp" : la changer n'a des impacts que limités). Lorsque /etc n'est pas accessible en écriture (cas courant dans la problématique embarqué), voilà donc ce qui se passe, de base (extrait de man passwd) :

DIAGNOSTICS
     Attempting lock password file, please wait or press ^C to abort

     The password file is currently locked by another process; passwd will
     keep trying to lock the password file until it succeeds or you hit the
     interrupt character (control-C by default).  If passwd is interrupted
     while trying to gain the lock the password changed will be lost.

     If the process holding the lock was prematurely terminated the lock file
     may be stale and passwd will wait forever trying to lock the password
     file.  To determine whether a live process is actually holding the lock,
     the admin may run the following:

           $ fstat /etc/ptmp

     If no process is listed, it is safe to remove the /etc/ptmp file to clear
     the error.

Et en effet, cette erreur apparaît dès que l'on a son / en lecture seule, ce que j'impose toujours dans des systèmes embarqués (par paranoïa naturelle). Malheur à celui qui eu son passwd testé avec un système de fichier monté en lecture-écriture pour cause de développement en cours (en l'occurrence, moi, on l'aura compris), avec de fait un bug non détecté. Et à vrai dire, avant de comprendre tout cela, il fallut pas mal de temps (dont deux remontées du même bug ou presque, avant de se rendre compte de l'erreur dans le test), et bien tâtonner : car il ne m'était pas venu à l'idée qu'un fichier servant de mutex puisse non pas être mis dans /var/run, comme dans tout système civilisé, mais dans /etc/ ; sans compter le fait que l'opération n'est pas atomique, mais on exclura l'idée de deux utilisateurs assez pervers pour lancer deux passwd quasiment en même temps (pourtant ipcs confirme l'existence d'un vrai système de sémaphores). Il y avait en réalité un autre mécanisme, caché : cela aurait été trop simple, sinon. En l'occurrence, passwd lançait tout seul pwd_mkdb, sans que l'option "-l" ne soit spécifié. Grâce à un système de liens, cela n'est pas bien grave, mais voilà : pwd_mkdb ne se lance qu'à condition de pouvoir ouvrir... /etc/ptmp. Car en réalité, passwd appelle le programme de construction de base à l'aide d'option de relocalisation "-d", et l'occurrence une mauvaise, en ce basant sur un autre système, qui au final donnait un "/etc" fatal. Croyant tout d'abord que c'était l'appel manuel de pwd_mkdb (on n'est jamais trop paranoïaque, et puis l'option "-l" n'est pas bien claire) qui échouait, et ayant découvert que la bibliothèque liée n'était pas la bonne, voilà comment on croît résoudre un bug alors qu'en fait on s'attaque à autre chose, et un mélange avec une partition en read-write pour cause de besoin de développement (au hasard, remplacer passwd, libutil.so et pwd_mkdb), puis oubliée dans cet état, amène à un drame (croire que c'est bon, puisque l'on a plus l'erreur d'avant, alors que ça ne l'est pas, puisqu'un bug en cache un autre).

"Errare humanum est, perseverare diabolicum"   (oui mais c'est passwd qui est diabolique, moi j'exorcise, j'le jure !)

J'eus donc l'idée, lors du développement, de retirer le fork de pwd_mkdb appelé depuis le code même de passwd, puisque l'exécutant de toute façon juste ensuite moi-même : le fichier de lock ptmp n'était alors plus supprimé ! De fait, deux exécutions d'affilée ne donnent pas la même erreur, et la seconde exécution donne lieu à une attente sur le fichier de lock présent alors qu'il ne devrait pas (plus) l'être (cf le man ci-dessus ; du coup, on peut croire être retombé dans la première configuration d'erreur, à tort). Ou comment brouiller encore plus les pistes. Et c'est ainsi que je perdis définitivement mon latin... Et pour finir : une erreur du pwd_mkdb forké, même inutilement, entraîne l'abandon de la modification du master.passwd (ce qui n'a pourtant pas grand rapport...), encore un comportement erratique de passwd (la fonction pw_error peut être ainsi appelé si l'édition du fichier échoue : c'est-à-dire si un appel via fork par sh de /bin/vi échoue... Tout dans la simplicité), de telle sorte que l'appel manuel "pour plus de sécurité" de pwd_mkdb travaillait sur un fichier non modifié, et regénérait donc la même base...

Que l'on se rassure, comme dans tous les contes, l'histoire se termine bien : une valeur en dur de répertoire inscriptible pour notre fichier de lock signalé à la main en des endroits stratégiques, et notamment dans le fork de la lib (oui, en dur...) résolut les problèmes. Ce n'est pas beau (euphémisme), mais ça marche. Et à vrai dire, dans une horreur diabolique pareille, cela ne se voit presque pas.

mardi, octobre 14 2008

berk, berk, berk

int  lm_match(struct lm_softc *);
int  wb_match(struct lm_softc *);
int  def_match(struct lm_softc *);

struct lm_chip {
        int (*chip_match)(struct lm_softc *);
};

struct lm_chip lm_chips[] = {
        { wb_match },
        { lm_match },
        { def_match } /* Must be last */
};

void
lm_attach(struct lm_softc *sc)
{
        u_int i, config;

        for (i = 0; i < sizeof(lm_chips) / sizeof(lm_chips[0]); i++)
                if (lm_chips[i].chip_match(sc))
                        break;

J'ai remis bout à bout les bons morceaux, dans la réalité, c'est bien plus espacé, sinon se serait trop lisible... (on remarquera que j'ai tout de même laissé les commentaires d'origine)

mercredi, septembre 24 2008

"je ne vous le fais pas dire !"

BTW, actually writing code for OKL4 feels rather scary: there seems to
be a real C library in there. After working with platforms like Nucleus
and an unnamed mobile phone OS that I can't talk about, this makes a
nice change. It's amazing how much you miss printf() if you can't get it...

(extrait de la ML d'OKL4, thread "[okl4-developer] Platform recommendations", vendredi dernier : un débutant découvre la magie d'OKL4, j'avais effectivement été tout aussi surpris de cette disponibilité du debug au printf de kernel en environnement paravirtualisé...)

mardi, septembre 9 2008

la macro comique du jour

C'est dans la compilation du kernel Linux, section "Device Drivers"->"Memory Technology Device (MTD) support" :

Enable chip ids for obsolete ancient NAND devices (MTD_NAND_MUSEUM_IDS)

lundi, septembre 8 2008

roooohhh, boulets !

$ echo "toto" | openssl dgst -md5 -verify public_key -signature thesig.sig
Error opening key file public_key
26004:error:02001002:system library:fopen:No such file or directory:bss_file.c:352:fopen('public_key','r')
26004:error:20074002:BIO routines:FILE_CTRL:system lib:bss_file.c:354:
unable to load key file
$ echo $?
0

No comment...

lundi, septembre 1 2008

bien choisir sa grammaire de fichier de conf

Ce n'est pas vendredi, mais aujourd'hui je suis un Saint, donc a priori, je suis au dessus de tout soupçon de trollage. OpenBSD est un système contestable à beaucoup de points de vue (admirez l'euphémisme), mais il y a des choses insupportables. C'est par exemple le cas du hostname.*. Ce fichier dont l'extension est le nom d'une interface réseau (nom qui change en fonction du driver employé : "contestable", disais-je), renferme les informations nécessaires à la bonne configuration de ladite interface : IP fixe ou dhcp, alias divers, activation (mais manifestement, pas de désactivation), et d'autres choses dont je n'ai pas la moindre idée de ce à quoi ça sert ("rtsol" ; les bridges quant à eux sont configurés par des fichiers spécifiques, comme l'indique un echo au cas où le mot clef serait rencontré). Prenons un exemple, avec une IP fixe :

$cat /etc/hostname.xl0
inet 192.168.20.201 255.255.255.0 192.168.20.255 description "Reseau Labo"
inet alias 192.168.20.202
inet alias 192.168.20.203
inet alias 192.168.20.204
inet alias 192.168.20.205

Comme on peut le voir, il n'y a qu'une seule ligne d'IP fixe, logique, en tête, suivie de plusieurs IP d'alias, toujours logique. On remarque que l'on commence par le mot clef "inet", à distinguer de "up", ou "dhcp" : jusqu'ici, tout va bien. Là où ça va mal, c'est le second token : soit une adresse IP, soit le mot clé "alias", qui sera alors suivi... d'une adresse IP. Ca ne sent pas bon. Déjà, on peut se dire qu'il y a plutôt intérêt à ce que l'adresse reste toujours indéfiniment sous forme de chiffres, car le jour où l'on veut puiser la correspondance lettrée dans /etc/hosts, et qu'un malin crée une machine "alias", ça risque de mal se passer.

Mais dans l'absolu, nous avons déjà un problème de parsing : "alias" or not "alias", that is the question. Et les gens de BSD ne se tracassent pas beaucoup, ils commencent leur parsing dans le script d'activation des interfaces /etc/netstart par un magnifique :

read af name mask bcaddr ext1 ext2

Ce qui, on l'admettra, est un peu bourrin. Car rien ne dit que le premier argument sera une adresse d'interface (AF), le second un nom (name), le troisième un masque (mask), etc. Résultat des courses, quelques lignes plus tard...

read dt dtaddr
if [ "$name"  = "alias" ]; then
   # perform a 'shift' of sorts
alias=$name
name=$mask
mask=$bcaddr
bcaddr=$ext1
ext1=$ext2
ext2=
else
alias=
fi
cmd="ifconfig $if $af $alias $name"

Oui, ça fait mal aux yeux...

dimanche, août 31 2008

développement habile (ou l'ôde à la moulinette et à l'enjoyment)

Yannick nous parle beaucoup de développement Agile (souvent très tôt le matin ou très tard le soir, et sans tricher je pense : je me sens donc obligé de répondre un dimanche :)  ). C'est très fashion, à tel point que ça contamine (heu, prophylaxise ?... À voir) même l'embarqué, ce joyeux monde d'électroniciens ratés, mais j'ai un très gros problème avec l'une de ses composantes qui s'applique à moi, ingénieur développeur/intégrateur : l'eXtreme Programming. Par exemple, on se souvient d'un tandem qui a essayé d'introduire la chose chez Thales il y a deux ans et demi, et qui a gagné le trophée de l'innovation pour ça (l'un des deux étant épitéen, et l'autre joueur d'échec, et tous deux bossant juste derrière moi, à Colombes). Mais surtout, j'ai souvenir d'avoir dû y passer moi-même, plus tard : et là, c'est moins drôle.

Mais commençons par un autre bout : oui, il faut des développeurs experts pour pondre les nombreux tests par fonction (heu, procédure : c'est surtout pour les langages pourris type C/Java qu'il est intéressant d'effectuer de tels tests -- mais c'est tellement répandu qu'on en oublie presque que le monde pourrait être parfait et en Caml  :)   ). Et c'est ici que vient s'insérer une anecdote d'un projet réalisé l'année dernière, dans mon ancienne boîte d'électroniciens (80% électronique / 20% informatique -- j'ironise à peine :)  ). Nous prenions pour réaliser les tests des gens en intercontrat, histoire de rentrer dans les coûts, parce que le client veut quelque chose qui marche avec une équipe, pas avec deux. Un jour, nous voyons un des deux développeurs de tests (sur la même machine, XP oblige) commencer à suer gravement : il était depuis deux jours sur un bug, qui arrivait de temps à autre sans prévenir. Et ce n'était pas que le programme à tester était mauvais, enfin, il ne savait pas trop : de temps en temps, segfault sans prévenir. Ciel ! Nous prenons un gdb, et effectivement, quand ça plante, ce n'est pas à moitié : la pile est totalement corrompue. Ça sent le scanf pourri.

Parce que lorsque l'on fait du test de ce genre, on utilise la libc à outrance, et on ne recode pas son parseur ("how many divisions ?"), alors on utilise des choses immondes comme scanf (il faudrait légiférer pour l'interdire définitivement). Bref, nous allons voir le code, et ma foi, il n'y a rien d'extraordinaire. C'est alors que vint l'idée : et si le tableau utilisé n'était pas forcément très ben alloué ? Et la réponse ne se fit pas attendre : "c'est quoi malloc ?". Ne riez pas : lorsque l'on est habitué à faire de l'informatique sur PIC et sans MMU, ça se comprend (enfin, presque, parce qu'un pointeur reste un pointeur, MMU ou pas, il vaut mieux s'assurer que ça ne pointe pas n'importe où). Et ils étaient deux à coder la même horreur côte à côte sur le même PC. Évidemment le bug du test absolument fatal fut rapidement corrigé. On pourra au passage se poser la question d'un bug encore plus mesquin (par exemple tableau alloué à un élément près) qui aurait pu avoir un effet de bord sur les résultats des autres tests (par exemple les déclarer faux alors qu'ils sont exacts, ou pire, les déclarer juste alors qu'ils sont incorrects).

Donc, il nous faut un ingénieur encore plus cher que le développeur, pour faire les tests. Déjà, trouver un développeur à peu près compétent par les temps qui courent, cela relève de l'exploit, et ce n'est pas gratuit ; mais en plus de ça, il faudra plus-que-doubler le coût final pour avoir quelque chose de potable. En ces temps de soldes permanentes que connaît le métier, j'ai comme un doute quant à la pertinence du business model (il va sans dire que le testeur ne doit jamais être la même personne que le développeur, c'est l'évidence même).

Ce que je n'aime pas avec la méthode agile, c'est qu'il s'agit d'une méthode. Ce qui dans le milieu veut dire : un dogme, avec ses gourous. Avant, c'était le cycle en V, que l'on louait beaucoup, malgré les 20% de projets réussis, et 80% de perte en ligne (lors d'une conférence, un commercial vantait son entreprise de génie logiciel qui arrivait au chiffre record de 35% de réussite dans ses projets -- et il était sérieux). Pourtant, le "bon sens" indique que ce système est digne d'un HEC pas bien dégrossi ne connaissant rien à l'informatique, et encore moins à la programmation. Calquer des modèles ne marche pas. Pourquoi ?

Parce que le métier de développeur relève de l'artisanat, et le concepteur d'un programme est un artisan. Il s'agit bien d'un art manuel. C'est pour cela qu'une armée de chinois ne prendra pas notre boulot (et une armée d'indiens non plus), sauf à ce que le travail ne relève plus de l'art, mais du "pissage de code", bête et méchant. De la robotisation, du travail automatique, taylorisé, déshumanisé. Le développement est une discipline artistique (avec des notions de beauté), il ne s'agit pas seulement d'être prompt et aisé dans ses mouvements. J'aime développer pour le vertige du kwrite blanc (contrairement aux chercheurs je ne développe jamais sur feuille : aucun moyen de compiler du papier n'a pour l'instant été trouvé) : il n'y a rien, l'immensité vierge, et il va falloir aller par touches successives mettre en place une cathédrale (à partir d'un bazar, c'est bien connu), échafauder un système complet et suffisant qui pourra effectuer des actions, qui aura des sorties voulues en fonction des ses entrées déterminées.

Tiens, du déterminisme ! Eh oui, à moins de développer le système du Deep Thought (qui est le nom de mon PC portable sur lequel j'écris, d'ailleurs), auquel cas un "return 42;" sera plus rapide, un programme informatique a des entrées attendues, et des sorties associées (en ce sens, on peut faire l'analogie avec une application mathématique : ne jamais oublier aussi que l'informatique est une branche les plus complexes des mathématiques, et dès lors que l'on sort de ce cadre, on fait de la peinture rupestre si l'on veut -- désolé pour les "développeurs" html --, mais certainement pas de la programmation !). Et nous pouvons donc tester directement en fonction de ces entrées et sorties depuis l'extérieur en mode "boîte noire" : c'est ce que l'on appelle une "moulinette de test", par exemple un script shell géant gérant toutes sortes d'appels (y compris devant générer des erreurs), et scannant la sortie pour nous répondre des OK ou des KO.

Il y a deux restrictions à cela : tout d'abord, le procédé peut paraître "grossier", en ce sens qu'il n'a pas la même profondeur de détail que le "test unitaire" (dont le nom est ambigu, d'ailleurs). On pourra répondre par la modularité nécessaire des grosses applications, par la nécessité de toujours mettre des hooks de debug sous macro (un "#if TEST", tout simplement), faisant des printf ou autres joyeusetés qui vont bien (attention cependant aux applications temps réel), et par le fait qu'empiler des briques préjugées correctes ne garantit pas non plus que le résultat final sera bon (pour répondre au présupposé agile). Ensuite, la limite de la moulinette se situe dans la programmation graphique.

Que l'on ne s'y trompe pas : l'XP a été créé pour et par des "génie logiciel" (attention aux acceptions de "génie" :)   ). Et l'on connaît les classeurs de centaines de page de test du style "cliquer ici, là, re-ici, etc, vous devez alors trouver blabla dans la zone de texte à droite". Sauf que c'est ici un faux problème : tout bon développement se fait en ligne de commande, et la partie graphique ne doit être qu'une interface qui... s'interface (c'est donc pour ça le nom ? :)   ). C'est exactement de cette manière que sont codés la plupart des applications sous KDE, ne serait-ce que parce que Qt est conçu dans cette optique précise.

L'habileté d'un programmeur ne se situe donc pas dans sa capacité à répondre à un schéma préétabli, dans lequel on le forcer d'entrer. Il est très politiquement incorrect en ces temps gouvernés par le seul aspect commercial ("combien de temps pour faire ceci ou cela", avec comme prérequis "le temps, c'est de l'argent") de déclarer cela, mais il ne faut pas oublier que le métier de développeur est celui de l'artisan. On peut tout à fait faire des choses et des bidules correspondant à des exigences précises mais que l'on qualifiera aisément d'usine à gaz tout de même (on en a quelques exemples dans le logiciel libre, et pour ne pas froisser Sophie on se contentera de citer Firefox). Un programme n'est pas une somme de fonctionnalités, c'est un tout, c'est une oeuvre (au sens juridique aussi, un bon indice !), une sculpture numérique, une composition (la musique, cette autre branche artisitque des mathématiques), ou une toile du XIXème (qui se fait par ajout successifs de couleur, par dégrossissement : il n'y a rien de mal à ne pas terminer immdiatement le codage d'une fonction quand cela s'avère nécessaire !) (je précise "XIXème" car je ne voudrais pas que l'on compare mon travail à du Picaso, Miro, ou autre horreur du genre commis le siècle suivant -- et qui se vendent aussi cher que cela aura pris peu de temps à faire, mais n'a pas son siège social à Redmond qui veut !).

Il fut un temps où la mode passa au tout-objet (en tant que méthode technique de développement, cette fois). Dijkstra avait alors déclaré : "Object-oriented programming is an exceptionally bad idea which could only have originated in California." Et pour nous convaincre qu'il n'aurait certainement pas trop aimé la tournure des choses actuelles : "Program testing can be used to show the presence of bugs, but never to show their absence!" Nous sommes toujours orientés dans cette optique, malheureusement.

Que l'on ne s'y trompe pas : le bon sens de la méthode Agile est à suivre (comme tout véritable bon sens), et l'on peut très bien arriver à des conclusions justes (du genre "on livre un programme qui marche, et dans les temps" -- qui est d'ailleurs aussi pris en hypothèse, méthode de raisonnement habituellement chère à la chimie, cette branche obscure de la cuisine) à partir d'hypothèses fausses ("la programmation n'est pas de l'artisanat et soumise à des méthodes fixes", ou autre), avec un raisonnement juste (table de vérité par ici). C'est incroyable, mais le "Release early, release often. And listen to your customers." ressemble très fortement à "Customer satisfaction by rapid, continuous delivery of useful software". Eh oui, le modèle du logiciel libre se fait pomper ! C'est peut-être parce que le logiciel libre marche extrêmement bien, non ? (dans une SSLL, ce serait un comble que de devoir convaincre son lectorat :)  ) Et que donc la méthode appliquée est certainement la meilleure qui soit ! C'est moins vendeur, c'est certain : du bazar  :). On pourrait peut-être renommer ça en "recuit simulé" ? (ah non, c'est déjà pris)

Allez, gardons le dernier mot pour feu notre Grand Gourou Dijkstra (dont il faudrait presque citer l'intégralité) :

The required techniques of effective reasoning are pretty formal, but as long as programming is done by people that don't master them, the software crisis will remain with us and will be considered an incurable disease. And you know what incurable diseases do: they invite the quacks and charlatans in, who in this case take the form of Software Engineering gurus.


update (30/10): après discussion avec Yannick, je suis totalement convaincu du bien fondé de la méthode (surtout qu'il existe à présent de vrais outils pour la mettre en place et coordonner, contrairement à ce que j'avais vécu il y a plusieurs années), même si je reste toujours sceptique vis-à-vis d'un gros développement en C, surtout embarqué/système. Mais je suis totalement ouvert à la méthode Agile en tant que gestion de projet, hors eXtreme Programming on l'aura compris  :). Ayant pu depuis tester le talent de son plus ardant défenseur à Linagora, je m'avouerais même fort intéressé : c'est bien connu, il n'y a que les imbéciles qui ne changent pas d'avis ! :)  ("cette pirouette rhétorique vous a été offerte par...")

- page 2 de 3 -