You are here -> Home steckdenis's journals » Mise à jour et fichiers dans Setup

Mise à jour et fichiers dans Setup

Le 23/03/2010 à 20h00 by steckdenis, See the Journals, 2 commentaries

Bonjour :) ,

L'installation, la suppression et la mise à jour sont des fonctions capitales pour un gestionnaire de paquets, mais elles ont été codées à la va-vite ! Ce ne sera bientôt plus le cas.

L'était des lieux est désastreux pour le moment, et vous pouvez en juger par vous-même en consultant le code de processthread.cpp, thread s'occupant de l'extraction d'un paquet.

Répétition de code, allure «à la shell» (en effet, ce code est directement porté du shell), hacks en tous genre, gestion douteuse des fichiers installés, bugs, tout y est.

Cette installation est tellement buggée que je ne peux même pas mettre Qt à jour sur ma clef USB. D'ailleurs, je vous déconseille fortement de mettre à jour le moindre paquet avant que Setup ne soit corrigé, vous risquez de corrompre la base de donnée Setup et de devoir réinstaller Logram :-° .

La solution

Une fonction m'a toujours manqué dans Setup : l'équivalent de «apt-file search». Super pratique quand on cherche un truc, pour les dépendances envers un fichier, pour trouver quel paquet contient une bibliothèque, etc.

Bref, c'est une fonction dont on a besoin, et j'ai donc réfléchi à comment l'implémenter.

La solution est relativement simple, enfin pas tellement, mais je vais tenter de l'expliquer. La «touche Setup» y est à nouveau présente (une BDD binaire O(1) :D ).

Fichiers dans la base de donnée du site web

Tout commence quand on inclus un fichier dans un dépôt. Setup lis ses fichiers, et les place dans la base de donnée SQLite ou MySQL du site web. Il fait ça bien, en utilisant la table «directory» et la table «file». Un dossier appartient à un dossier, un dossier peut appartenir à un fichier. Un FS quoi :p .

Cette architecture permet très facilement de rechercher un paquet en fonction d'un fichier, directement depuis le site web. De plus, il est extrêment simple d'afficher les fichiers d'un dossier :

1
2
3
4
5
6
7
8
9
SELECT file.name directory.path package.name 
    FROM packages_file file 
    LEFT JOIN packages_package package ON package.id=file.package_id 
    LEFT JOIN packages_directory directory ON directory.id=file.directory_id 
    WHERE file.name LIKE '%bla%';
SELECT file.name directory.path 
    FROM packages_file file 
    LEFT JOIN packages_directory directory ON directory.id=file.directory_id 
    WHERE file.package_id=34;

La première requête permet d'afficher «le fichier /usr/share/logram/themes/bla.png est contenu dans logram-de-data». En effet, un dossier a un nom («share») et un chemin («usr/share»), permettant de facilement remonter l'arbre sans trop de requêtes :) .

La deuxième requête permet de liste tous les fichiers d'un paquet donné.

Ce qu'on donne à manger à Setup Update

Quand on fait «setup export», les dossiers sont lus ainsi que leurs fichiers. Il est alors extrêmement intéressant d'avoir le nom des dossiers en BDD en plus de leur chemin.

En effet, le format de ce fichier est extrêmement compact. Alors que Debian utilise un machin collosal du type :

1
2
/usr/share/truc/brol                        paquet
/usr/share/brol/truc                        paquet

Logram utilise un format bien plus léger :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
:usr
:share
:truc
paquet|0|brol
::
:brol
paquet|0|truc
::
::
::

Le tout se compresse super bien en XZ (une liste de 1840 octets occupe 552 octets en XZ). De plus, c'est très facile à parser (le premier caractère permet de savoir si on entre dans un dossier, si le deuxième est aussi ':', alors c'est qu'on sort, sinon on a un fichier).

Le '0' est également intéressant. Ce sont les flags du fichier. Oui ! Des flags :D ! Par exemple, un paquet pourra placer le flag «backup» sur un fichier devant systématiquement être sauvegarde en .old par Setup à la mise à jour, ou «ignoreuser», pour un fichier systématiquement écrasé même si l'utilisateur l'a modifié, ou alors des informations sur le calcul des deltas (à venir dans ... longtemps :-° ), comme «dontdelta» pour un fichier qui change trop, «binary» pour utiliser un truc genre bsdiff, «text» pour utilise le diff UNIX qui prend plus de place, mais permet de patcher des modifications locales, etc.

Bref, Setup entre dans la court des grands, très grands :) .

Base de donnée binaire

Une fois cette liste rapatriée par Setup upgrade, il faut savoir quoi en faire. J'ai décidé d'utiliser un format binaire pour stocker les fichiers. En effet :

  • On y accède beaucoup en lecture
  • On n'ajout jamais rien (expliqué plus tard)
  • C'est très facile à représenter.

Le format sera une simple liste d'entrées _File, dont un flag permettra de savoir que c'est un dossier, un autre s'il est installé.

Ainsi, comme on a tous les fichiers possibles et imaginables dans la BDD, il suffit pour «installer» un fichier de le marquer comme installé :) . Une liste, au même format que ci-dessus, permettra de garder la trace des fichiers locaux (mises à jour qui font disparaitre des fichiers, fichiers .lpk hors d'un dépôt installés, etc).

Cette liste des fichiers permettra de bien mieux gérer la suppression des vieux fichiers lors d'une mise à jour (donc pas supprimer trop de fichiers s'il y a eu un petit problème), les flags comme «ne pas supprimer ce fichiers lorsque son paquet est supprimé», «ne pas supprimer sauf si on purge (fichiers de conf)», etc.

Recherche sur le site web

La base de donnée du site web permettra, comme dit plus haut, de facilement rechercher le paquet contenant un fichier, ou les paquets contenant un fichier correspondant à un pattern, etc. Il sera également possible de voir la liste de tous les fichiers d'un paquet, regroupés dans un très bel arbre.

Du côté de la GUI graphique de Setup (à venir une fois KDE empaqueté, donc vers la fin de l'année scolaire pour ne pas trop m'avancer), on aura également l'arbre des fichiers installés par un paquet, même si le paquet n'est pas installé (fonctionnalité que peux de gestionnaires de paquets graphiques ont).

Une fonctionnalité spéciale Setup, mais également présente dans RPM je pense, est le support des fichiers «virtuels». Par exemple, si on installe foobar-1.0, le paquet contient un certain nombre de fichiers. Par contre, le programme qu'il contient crée par exemple /var/foobar/data.bin. Un petit ajout dans les métadonnées permettra de dire que ce fichier, non présent dans l'archive .tlz, devra être supprimé :

1
2
3
4
5
<!-- file et non pas files -->
<file name="/var/foobar/data.bin" /> <!-- Supprimé à la désinstallation -->
<file name="/var/foobar/data2.bin">
    <flag name="dontremove" value="1" /> <!-- Supprimé seulement par purge -->
</file>

Conclusion

Je consacre cette semaine à cette amélioration, du fait qu'elle est critique en ce sens que Setup est difficilement utilisable à grande échelle pour le moment. Ensuite, je m'occuperai à créer un LiveCD (avec GRUB 2 et non-pas isolinux, pour profiter d'un beau menu graphique et animé, si possible). Si je réussi à finir le LiveCD avant le 4 avril, il se pourrait que j'améliore quelques détails dans Setup :

  • Passage de QString à QByteArray. En effet, Setup est actuellement ralentit et consomme trop de RAM à cause des QStrings (deux octets par caractère, des conversions à l'affichage et dans d'autres opérations). De jolies QByteArrays contenant de l'utf8 pur, comme on trouve dans tous les fichiers des systèmes modernes, accélérera bien des choses.
  • Documentation Doxygen de toutes les classes. J'ai en effet du me replonger dans le code de quelques fonctions pour les comprendre :-° .
  • Débogage.

Après-conclusion : les 9 commits d'hier

Hier, j'ai déjà commencé un peu de travail. Je n'avait pas encore touché à l'objet de ce journal (ça, c'était aujourd'hui : j'ai codé les étapes 1 et 2 : setup include et setup export).

Par contre, j'ai effectué un léger nettoyage de Setup :

  • Détection de l'architecture plus propre, au moment de la compilation (donc on n'utilise plus une QString pour stocker l'architecture, le compilateur la voit comme une constante #definée)
  • Ne pas construire un paquet de la mauvaise archi. Par exemple, un paquet "any" ou "all" est toujours construit ("any" devient "i686" par exemple), un paquet "i686" ne sera construit que quand on est en i686. Ainsi, il sera possible d'inclure des fichiers différents suivant une architecture, ou d'avoir des postinst différents, etc.
  • Sémantique correct des Replace (type de dépendance). Avant, Replace était égal à Conflict, ce qui peut se comprendre, mais ne sert à rien. Maintenant, Replace = Conflict + Provide. Par exemple, si A est en conflit avec B, installer A désinstallera B et ce qui en dépend. Si A remplace B, B sera désinstallé, pas ce qui en dépend. Cas réel : vim-full remplace vim-tiny (un Vim complet remplace le petit, il ne sait pas fonctionner avec lui (conflit de fichiers), mais tout ce qui demande vim-tiny marche aussi avec vim-full). Même chose pour les trucs du genre "fluxbox" et "fluxbox-svn".
  • Petit nettoyage : un paquet avait des flags et un état, deux int32, donc 8 octets. Maintenant, l'état est une paire de flags, donc on gagne 4 octets de BDD binaire par paquet. Comme une BDD plus petite est une BDD plus rapide (moins de déplacement de la tête du disque quand on passe d'un paquet à l'autre, puisqu'ils sont plus proches), c'est une excellente chose, et le code est mieux :) .
  • Feature très demandée par linkdd :D : les progressions affichent plus d'information. Au lieu d'afficher «Opération sur initng», on a des messages du type «Installation d'initng à la version 0.6.90», «Suppression d'initng à la version 0.6.90», «Mise à jour d'initng à la version 0.6.90 vers 0.6.91». En effet, un «void *data» fait partie des progressions, ce qui permet de passer un paquet (donc la GUI affichera par exemple le titre et la description courte du paquet en cours d'installation), ou n'importe quoi, dès que le besoin s'en fait ressentir (voir même une structure contenant plein de choses). Bref, pratique :) .
  • Des tests unitaires, pour le moment pas très poussés, et compilés directement dans Setup. J'ai recodé une fonction, parseVersion pour les curieux, pour qu'elle prenne des QByteArrays et pas des QStrings. Setup update est maintenant plus de 20% plus rapide :D . J'ai recodé cette fonction en C pur, beau, propre, au lieu d'utiliser plein de QString::contains ou QString::split.

Voici la nouvelle fonction parseVersion. Si vous n'avez pas git pullé depuis quelque temps, vous trouverez l'original vers la fin de libpackage/packagesystem.cpp :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
int Logram::PackageSystem::parseVersion(const QByteArray &verStr, QByteArray &name, QByteArray &version)
{
    const char *s = verStr.constData();
    char c, c2;
    int namesize = 0, versionsize = 0, op = DEPEND_OP_NOVERSION, pos = 0, len = verStr.size();
    const char *n = 0, *v = 0;

    // Le nom
    n = s;
    while (*s != 0 && pos < len)
    {
        c = *s;

        if (c == '>' || c == '<' || c == '!' || c == '=')
        {
            break;
        }

        s++;
        namesize++;
        pos++;
    }

    if (*s == 0 || pos == len)
    {
        // Juste un nom
        name = QByteArray(n, namesize);
        return op;
    }

    // L'opération
    // Ici, c est soit égal à >, soit à <, soit à !, soit à =.
    // Il suffit de comparer le caractère suivant
    s++;            // s pointe sur le premier caractère de la version, ou =, ou \\0 s'il y a un problème
    pos++;
    c2 = *s;

    if (c2 == 0 || pos == len)
    {
        name = QByteArray(n, namesize);
        return op;
    }

    if (c2 == '=')
    {
        // Avancer d'un caractère pour la version
        s++;
        pos++;
    }

    switch (c)
    {
        case '>':
            op = (c2 == '=' ? DEPEND_OP_GREQ : DEPEND_OP_GR);
            break;
        case '<':
            op = (c2 == '=' ? DEPEND_OP_LOEQ : DEPEND_OP_LO);
            break;
        case '!':
            op = DEPEND_OP_NE;  // a!=b ou a!b est pris, side effect
            break;
        case '=':
            op = DEPEND_OP_EQ;  // a=b ou a==b est pris, side effect
            break;
    }

    // La version, simplement prendre de *s au bout
    v = s;

    while (*s && pos < len)
    {
        s++;
        versionsize++;
        pos++;
    }

    // Retour
    name = QByteArray(n, namesize);
    version = QByteArray(v, versionsize);
    return op;
}

Voilà, long journal, pour vous dire qu'il est normal que peu de paquets arrivent dans le dépôt (de nouveaux sont faits mais pas encore testés, comme par exemple une nouvelle version de Linux, qui ne contient pas trop de modules, ou alors une version de Qt qui livre les fichiers .desktop de QtAssistant & co, ou alors Logram DE qui supporte le menu Applications, etc :) ).

À plus.

Commentaries

Author Message
steckdenis
# le 24/03/2010 à 21h10
Ça marche !
Avatar
Group : Administrateur

Bonjour :) ,

Voici le résultat du codage de ces deux derniers jours : l'importation de la liste des fichiers marche en BDD.

J'en ai également profité pour largement améliorer l'affichage que fait Setup de ces fichiers, pour donner une touches plus finie. Le paramètre -W, qui désactive la couleur, permet également d'utiliser l'ancien affichage, plus facilement réutilisable dans un script.

Setup tout en couleurs

Non, l'image ne se répète pas en haut, j'ai simplement lancé la commande deux fois et scrollé ma Konsole vers le haut un peu plus que nécessaire, mais ça vous permet indirectement de voir tout l'arbre même s'il est coupé en bas (coupé juste au-dessus de la ligne man, reprise en haut de l'image).

KDE le fait depuis 10 ans.

danman
# le 31/03/2010 à 20h33
Heureux d'être là
Group : Member

tu me donnes envie de reprendre le travail de packer (si tu t'en souviens) :unsure: