Cette astuce fonctionne avec la version 1.0 ou supérieure de LXR parce que le contrôle précis de l'ordre d'application des règles a exigé une réécriture de la fonctionnalité 'maps'. Avec les versions antérieures, la disposition de l'en-tête des pages sera horriblement embrouillée et le résultat final ne sera pas garanti (il dépend de l'ordre d'extraction des clefs qui peut varier d'exécution en exécution selon la documentation Perl). Pour bien comprendre cette astuce, lisez d'abord comment établir les chemins d'inclusion, comment définir des variables conditionnelles et comment réécrire dynamiquement des chemins de fichier.

Liens d'inclusion habituels

Le noyau Linux a été porté sur de nombreuses architectures informatiques. Toutefois, il n'existe qu'un arbre source englobant toutes les "fonctionnalités" et "machines" possibles.

Ici, le mot fonctionnalité est pris dans le sens de "service" disponible, comme ACPI, E/S, réseau, gestion de la mémoire, …

Pour être gérable, l'arbre source est divisé en répertoires plus petits, contenant éventuellement des sous-répertoires pour garder les choses bien en ordre:

Le répertoire include/ lui-même possède une organisation similaire avec beaucoup de sous-répertoires nommés selon la fonctionnalité. Les définitions non rattachées à une fonctionnalité sont rangées dans le sous-répertoire linux/.

Un fichier source typique commence ainsi:

#include <linux/capability.h> #include <linux/file.h> #include <linux/times.h> #include <net/sock.h>

Comme on le voit, le répertoire include/ est implicite. Son emplacement est passé au compilateur par une option. Nous devons donc imiter cela pour permettre la construction d'hyperlien sous les directives #include. C'est possible par le recours à un paramètre 'incprefix' dans lxr.conf:

, 'incprefix' => [ '/include' ]

Liens d'architecture

Les parties spécifiques d'une architecture sont rangées dans le répertoire arch/ dans leurs propres sous-répertoires (alpha/, arm/, powerpc/, x86/, …). Mais, ces parties ne sont pas directement incluses par le code indépendant de l'architecture. Au lieu de cela, la directive est écrite #include <asm/...> comme dans l'exemple suivant:

#include <linux/mount.h> #include <asm/uaccess.h> #include <asm/div64.h>

Le répertoire abstrait asm/ est associé à un répertoire d'architecture réel par le système de gestion de configuration à travers la spécification d'une option au compilateur. Tous les répertoires d'architecture contiennent un sous-répertoire include/asm/ où toutes les définitions .h sont stockées. Si nous sommes intéressés par l'architecture arm, nous pouvons compléter 'incprefix' de ci-dessus par:

, 'incprefix' => [ '/include' , '/arch/arm/include' ]

Et si nous voulons comparer avec la version x86? Reconfigurer lxr.conf pour une lecture de deux minutes n'est pas particulièrement enthousiasmant.

Par bonheur, nous pouvons utiliser une fonctionnalité très pratique de LXR: 'maps' avec substitution de variable.

D'abord, nous créons une variable dont la valeur sera l'architecture cible (ou plutôt, le nom du sous-répertoire pour l'architecture). Appelons-la 'a' pour architecture:

, 'variables' => { 'v' => { … } # Variable des versions (obligatoire) , 'a' => { 'name' => 'Architecture' , 'range' => [ qw(alpha arm powerpc x86) ] , 'default' => 'arm' } } À ce stade, le contenu de 'range' doit être tapé à la main. Nous risquons d'omettre une architecture ou de taper le nom d'une inconnue. Plus loin, une astuce est donnée pour en recueillir automatiquement la liste à jour.

Quand LXR est lancé, cette variable est affichée dans l'en-tête de la page. Sélectionnez l'architecture cible et cliquez sur le bouton Change pour modifier la variable.

Ensuite, la règle 'incprefix' d'architecture doit être réécrite pour faire référence à une architecture virtuelle:

, 'incprefix' => [ '/include' , '/arch/%=ARCH=%/include' ] Le nom %=ARCH=% a été choisi avec le vœu pieux que ce ne sera jamais le nom d'un répertoire réel. Si ce n'était pas le cas, ce nom devrait être changé, sinon des choses bizarres pourraient se produire en association avec le paramètre suivant.

Enfin, une règle de réécriture de chemin est appliquée juste avant le test d'existence du fichier avec la valeur de la variable en paramètre:

, 'maps' => [ '^/arch/%=ARCH=%/' => '/arch/$a/' ]

Liens de variantes d'architecture

Les paramètres de configuration précédents sont suffisants pour les architectures informatiques "classiques", mais, de façon évidente, ne répondent pas à toutes les exigences des architectures embarquées, comme arm. Celles-ci offrent des variantes dans les processeurs, cartes, circuits périphériques, … qui présentent de véritables défis pour la configuration de LXR.

Les architectures avec variantes connues dans le noyau 3.x sont arm, avr32, blackfin, cris, mn10300 et um. Malheureusement, il n'y a pas de méthode d'identification automatique.

Prenons le cas arm comme exemple. L'implémentation Linux-arm doit prendre en compte les variantes machine et plateforme. Deux nouvelles variables sont alors créées:

, 'variables' => { 'v' => { … } # Variable des versions (obligatoire) , 'a' => { 'name' => 'Architecture' , 'range' => [ qw(alpha arm powerpc x86) ] , 'default' => 'arm' } , 'arm_mach' => { 'name' => 'ARM machine' , 'when' => '"$a" eq "arm"' , 'range' => [ qw(at91 bcmring clps711x davinci) ] # et bien d'autres } , 'arm_plat' => { 'name' => 'ARM platform' , 'when' => '"$a" eq "arm"' , 'range' => [ qw(iop mxc nomadik) ] # et quelques autres encore } } La clause 'when' est une fonctionnalité 1.0 évitant l'affichage d'une variable en dehors d'un contexte défini. Ici, les variables n'apparaissent à l'écran que lorsque la variable 'a' est égale à arm.

Les fichiers spécifiques de machine sont référencés par #include <mach/…> et ils sont logés dans le répertoire arch/arm/mach-xxx/include/mach/xxx est l'une des valeurs définies dans 'range' de 'arm_mach' (respectivement 'arm_plat' pour les fichiers spécifiques de plateforme).

En laissant de côté la question de 'incprefix' pour l'instant, les règles suivantes devraient faire l'affaire:

, 'maps' => [ '^/arch/arm/%=LEVEL2=%/' => '/arch/arm/mach-${arm_mach}/' , '^/arch/arm/%=LEVEL2=%/' => '/arch/arm/plat-${arm_plat}/' ]

ÇA NE MARCHE PAS!

Parce que l'effet des règles 'maps' est cumulatif: après application de la première règle, %=LEVEL2=% a été remplacé et, par conséquent, la deuxième règle n'est plus reconnue puisque %=LEVEL2=% a disparu.

Ceci implique que nous devons forcer deux chemins d'inclusion différents. La configuration correcte est

, 'incprefix' => [ '/include' , '/arch/%=ARCH=%/include' , '/arch/%=ARCH=%/%=LVL2A=%/include' , '/arch/%=ARCH=%/%=LVL2B=%/include' ] , 'maps' => [ '^/arch/%=ARCH=%/' => '/arch/$a/' , '^/arch/arm/%=LVL2A=%/' => '/arch/arm/mach-${arm_mach}/' , '^/arch/arm/%=LVL2B=%/' => '/arch/arm/plat-${arm_plat}/' ]
CAVEAT! Toutes les combinaisons machine/platform ne sont pas valides. Si des directives #include ne reçoivent pas d'hyperlien, vérifiez si les variables 'arm_mach' et 'arm_plat' décrivent bien une configuration existante.

Des règles semblables peuvent être conçues pour les autres architectures.

Inventaire automatique des architectures

À partir de la version 1.0, LXR est fourni avec un script qui recueille les noms des architectures et de leurs variantes. Lancez depuis le répertoire racine de LXR:

$ cd /repertoire/racine/LXR $ ./scripts/kernel-grab-vars.sh /arbre/source/noyau

Les options possibles sont --help, --verbose, --erase et --suffix=s.

Le résultat du script est un ensemble de fichiers *_list.txt dans le répertoire custom.d/ adaptés à une lecture automatique dans la configuration principale.

Synthèse

Les paramètres suivants sont recommandés pour la navigation dans le noyau Linux:

, 'variables' => { 'v' => { 'name' => 'Version' , 'range' => [ readfile('custom.d/version_list.txt') ] } , 'a' => { 'name' => 'Architecture' , 'range' => [ readfile('custom.d/arch_list.txt') ] , 'default' => 'x86' } , 'arm_mach' => { 'name' => 'ARM machine' , 'when' => '"$a" eq "arm"' , 'range' => [ readfile('custom.d/arm_mach_list.txt') ] } , 'arm_plat' => { 'name' => 'ARM platform' , 'when' => '"$a" eq "arm"' , 'range' => [ readfile('custom.d/arm_plat_list.txt') ] } , 'avr32_mach' => { 'name' => 'AVR32 machine' , 'when' => '"$a" eq "avr32"' , 'range' => [ readfile('custom.d/avr32_mach_list.txt') ] } , 'blackfin_mach' => { 'name' => 'Blackfin machine' , 'when' => '"$a" eq "blackfin"' , 'range' => [ readfile('custom.d/blackfin_mach_list.txt') ] } , 'cris_arch' => { 'name' => 'CRIS architecture' , 'when' => '"$a" eq "cris"' , 'range' => [ readfile('custom.d/cris_arch_list.txt') ] } , 'mn10300_proc' => { 'name' => 'MN10300 processor' , 'when' => '"$a" eq "mn10300"' , 'range' => [ readfile('custom.d/mn10300_proc_list.txt') ] } , 'mn10300_unit' => { 'name' => 'MN10300 unit' , 'when' => '"$a" eq "mn10300"' , 'range' => [ readfile('custom.d/mn10300_unit_list.txt') ] } , 'um_sys' => { 'name' => 'UM system' , 'when' => '"$a" eq "um"' } } , 'incprefix' => [ '/include' , '/arch/%=ARCH=%/include' , '/arch/%=ARCH=%/%=LVL2A=%/include' , '/arch/%=ARCH=%/%=LVL2B=%/include' ] , 'maps' => [ '^/arch/%=ARCH=%/' => '/arch/$a/' , '^/arch/arm/%=LVL2A=%/' => '/arch/arm/mach-${arm_mach}/' , '^/arch/arm/%=LVL2B=%/' => '/arch/arm/plat-${arm_plat}/' , '^/arch/avr32/%=LVL2A=%/' => '/arch/avr32/mach-${avr32_mach}/' , '^/arch/blackfin/%=LVL2A=%/' => '/arch/blackfin/mach-${blackfin_mach}/' , '^/arch/cris/%=LVL2A=%/' => '/arch/cris/arch-${cris_arch}/' , '^/arch/mn10300/%=LVL2A=%/' => '/arch/mn10300/proc-${mn10300_proc}/' , '^/arch/mn10300/%=LVL2B=%/' => '/arch/mn10300/unit-${mn10300_unit}/' , '^/arch/um/%=LVL2A=%/' => '/arch/um/arch-${um_sys}/' ] CAVEAT! Pour contourner un problème Debian, utilisez des noms de fichiers absolus comme argument de readfile. L'assistant de configuration le fait à partir de la version 2.0.3.

Ceci peut être automatiquement généré par le script de configuration initiale:

$ cd /LXR/root/directory $ ./scripts/configure-lxr.pl --add --conf-out=lxr.conf lxrkernel.conf

Utilisez l'option --add si vous avez déjà configuré LXR pour un autre arbre. Si vous voulez vous servir de LXR seulement sur le source du noyau, la procédure complète de configuration est effectuée par:

$ ./scripts/configure-lxr.pl --conf-out=lxr.conf lxrkernel.conf

Si vous avez donné un --suffix=s au script kernel-grab-vars.sh, vous devez d'abord remplacer manuellement le suffixe par défaut par votre suffixe personnalisé dans ce fichier.

N'oubliez pas d'exécuter le script initdb.sh pour créer la base de données associée:

$ ./custom.d/initdb.sh

Copiez le fichier de configuration où LXR s'attend à le trouver:

$ cp custom.d/lxr.conf lxr.conf

Tout est prêt pour genxref.