Introduction Expert-solutions est éditeur de sa propre version de CLAIRE nommée « XL CLAIRE » et créée il y a 4 ans. Au cours des différents projets menés par XL, CLAIRE a pris une place de plus en plus dominante dans les développements et devient aujourd’hui le seul langage utilisé pour la réalisation des (nouveaux) projets : XL fait du « tout CLAIRE ». Pour assurer cette position industriellement, XL à développé des compétences sur les couches basses du langage CLAIRE afin de pouvoir comprendre, maîtriser et corriger « rapidement » tout type de dysfonctionnement. Aujourd'hui, XL peut intervenir à la fois sur le noyau/GC de CLAIRE, sur les « meta » et autres modules et dans une moindre mesure sur le compilateur CLAIRE. XL à mis en place son propre environnement de production de code. A l'origine intégré dans l'IDE Eclaireur pour win32, la gestion des projets multi-modules et le partage des modules a été re-développé en CLAIRE et accessible à la ligne de commande. Cette étape a été nécessaire car la plateforme de développement et déploiement préférée est aujourd'hui UNIX. XL réalise des projets type « application internet » et a développé toute une infrastructure dédiée à ce type d'application. Cela comprend la génération dynamique de pages HTML multilingues, la production de documents PDF signés numériquement et l'envoie de message électroniques. De telles applications prennent la forme d'agent CLAIRE placé derrière un serveur web. Enfin XL est très sensible aux problèmes de performances, sur les algorithmes de recherche de solution d'une part, et sur les problèmes de montée en charge des serveurs derrière lesquels peuvent se trouver de multiples agents d'autre part. Vers le noyau XL Dans ce document on fait référence au noyau développé par Yves Caseau par <ycs>. Le noyau XL est une évolution du noyau <ycs>, certaines fonctionnalités ont étés ajoutées d'autre modifiées mais sans jamais changer la sémantique du langage. * Traces du noyau Tous les traces du noyau XL (par exemple le trace GC) sont systématiquement redirigés vers ctrace() plutôt que sur stdout. On le vera plus loin il est nécessaire dans un environnement CGI que la sortie (redirigée) du processus ne soit pas polluée. Pour cela le noyau XL dispose de sa propre méthode de trace analogue à printf. * La classe module Un slot verbose a été ajouté à la classe module dans le noyau XL. Ainsi chaque module peut avoir sa propre politique de trace. C'est particulièrement pratique pour filtrer les traces d'applications ayant 10 voir 20 modules. Par défault le champ verbose d'un module est à false ce qui correspond au comportement du noyau <ycs>. Ce champ peut prendre les valeur suivantes : • • • • false (défault) : identique à <ycs> true : tout les traces du module sont issus <int> : comme si la verbose système valait <int> pour ce module Interval : les traces sont issus seulement si leur niveau appartient à l'intervale La façon dont est traitée l'instruction de trace a donc été modifiée. De plus, le message de trace est automatiquement préfixé par : [<module>:<niveau>] le message du trace Si, de plus, le processus qui génère le trace est un processus « forké » alors le prefixe devient : [<pid>][<module>:<niveau>] le message du trace Dans le noyau XL, les modules ont également un slot version qui permet de partager (le partage de module est décrit en deuxième partie) plusieurs version d'un même module. La version d'un module est une chaîne de caractère. Enfin le slot made_of d'un module peut contenir dans XL CLAIRE des fichiers .cpp ou .h. Les .cpp seront ajouter au makefile lors de la compilation et les .h seront inclus dans le .h du module généré (via compiler.headers). * Séquences d'échappement Le Reader XL CLAIRE supporte des séquences d'échappement pour exprimer des nombres des chaînes ou des caractères dans les base binaires octale et hexadécimales. Une séquence est introduite par le caractère anti-slash « \ », suivi d'un indicateur de base : Pasted Graphic 9.tiff ¨ * Représentation des chaînes de caractère Les chaînes <ycs> sont à l'image des string en C/C++. Pour en connaître la longueur on utilise strlen, ces chaînes ne peuvent contenir de caractère nul. C'est une limitation quand on manipule des données binaires (arbitraire). Dans le noyau XL, on adopte le style pascal pour la représentation des chaînes CLAIRE. Dans cette représentation, la longueur d'une chaîne est stockée en entête de la zone mémoire qui contient la chaîne, par exemple la chaîne "toto" est représenté ainsi : Pasted Graphic 10.tiff ¨ La longueur d'une chaîne se calcule quasi-instantanément avec ce type de représentation alors que strlen doit parcourir la chaîne entièrement pour en donner la longueur. Les chaînes C/C++ restent cependant supportées pour toutes les chaînes qui résident en dehors de Cmemory (cas des chaînes statiques ou importées). Les API de manipulation de chaîne ont donc été modifiées pour utiliser la nouvelle API length_string : int length_string(char *s) { return CLMEM(s) ? *((int*)s - 1) : strlen(s); } Note : on réserve systématiquement un caractère nul en fin de chaîne, ainsi les chaînes CLAIRE sont entièrement compatibles avec les chaînes C/C++ et peuvent êtres passées en argument à des appels fonctionnels de bibliothèques externes sans causer de dommage. * Destructeurs: freeable_object Le besoin d'un destructeur a été évoqué sur le forum claire-language et implementé dans le noyau XL pour un nouveau type d'objet: freeable_object <: ephemeral_object( freeme?:boolean = false) Un objet de type freeable_object est éphémère, lorsque le GC détruit une instance de ce type il appelle free! donnant l'opportunité de faire un nettoyage, par exemple : long_double* <: import() long_double <: freeable_object(value:long_double*) close(self:long_double) : long_double -> (sel.value := externC( "(long double*)::malloc(sizeof(long double))", long_double*), self) free!(self:long_double) : void -> externC("::free(self->value)") Une liste d'instances de freeable_object est maintenue par le noyau. Lors d'un GC, le conteneur de cette liste est d'abord marqué, puis, à la suite de l'opération de marquage les instances de cette liste qui n'ont pas étés marqués sont marqué « à la main » et leur slot freeme? est mis à true de sorte qu'a la fin du GC (lorsque la mémoire est dans un état sain) on peut reparcourir cette liste et appeler free! pour les objets qui ont freeme? à true. L'appel à free! peut générer un GC (récursif), auquel cas le traitement ci-dessus n'est pas effectué, l'ensemble des freeable_object sont marqués et leur slot freeme? reste inchangé. Note : Lorsque CLAIRE se termine free! est appelé pour tous les freeable_object encore en mémoire afin de terminer proprement l'exécution. * freeContent Dans le noyau XL lorsqu'un bag est ré-alloué (par exemple à la suite d'une insertion) l'ancien contenu est désalloué immédiatement. Une méthode freeContent à été ajoutée au GC à cet effet. * GC & congestion mémoire Egalement évoqué sur le forum claire-language, le noyau XL apporte une solution au problème de congestion mémoire. L'idée principale est de profiter de la gestion efficace de la mémoire virtuelle (VM) sous une architecture UNIX. Les schémas ci-dessous illustrent la congestion : Pasted Graphic 11.tiff ¨ Lorsque l'option « -auto » est utilisée et qu'une congestion mémoire intervient, la mémoire est automatiquement augmentée. Avec cette option, le plus grand block possible est alloué lors de l'initialisation de Cmemory (comme si « -s 9 j » était donné). Cependant CLAIRE est contraint dans un espace plus petit conformément à l'option « -s i j ». En cas de congestion de la mémoire (chunk ou object) on opère à un simple décalage de la borne de la mémoire concerné ce qui est instantané et ne provoque aucune ré-allocation dynamique. On prend en compte deux type de congestion : • spatiale : il n'y a de toute façon pas assez de place pour alloué un objet donc on augmente la mémoire • temporelle : on passe plus de temps à faire des GC que du calcul donc on donne plus d'espace au programme afin d'éviter de passer trop de temps dans le GC. Note 1: Sous win32, ceci est totalement inefficace car la gestion de la mémoire virtuelle est en fait physique! En revanche sous UNIX ceci est particulièrement efficace: temps que la mémoire n'est accédée qu'en lecture aucune page mémoire n'est effectivement allouée. Note 2: En « -auto » les différentes piles allouées par le noyau (GC, exécution et monde) supportent la congestion. Les piles sont automatiquement ré-allouées pour prévenir un dépassement. Seule la table des symboles ne supporte pas la congestion. Note 3: Aucun mécanisme de décongestion n'est supporté: la mémoire utilisée ne peut qu'augmenter. * API supplémentaire de la librairie C Un certain nombre d'API de la lib C ont étés ajoutées au noyau XL. Ces API concernent les domaines suivants: • • • • • • • accès au système de fichier manipulation de l'environnement du processus manipulation de chaîne de caractère manipulation de date/time création et attente de processus fork port réseau (socket) Ces API ont historiquement étés ajoutées directement au noyau du fait de leur implémentation en C++. Cela augmente la taille du noyau et il est aujourd'hui question d'en créer un module dédié (voir roadmap). Note : Fork est le moyen naturel sous unix pour créer un nouveau processus (attention: pas de fork sous win32). C'est un mécanisme très intéressant pour CLAIRE au regard du metaLoad. En effet on peut avoir un processus père qui va effectuer le metaLoad puis attendre un événement, les traitements effectués en réponse à cet événement peuvent ensuite être réalisés dans un processus « forké » (pour lequel le metaLoad à déjà été effectué)... * Signaux Le noyau XL supporte les signaux et on peut écrire ses propres services d'interruption en CLAIRE. Par définition les signaux sont des événements asynchrones et il est dangereux pour l'intégrité de la mémoire de CLAIRE de les traiter de cette manière sans aller droit au crash (cas d'une interruption pendant un newChunk!). Le choix qui a été fait est de dissocier l'interception du signal de sont traitement. Pour cela le noyau maintient une cache de signaux qui seront redistribués depuis un point d'exécution approprié au routines d'interruption. Ces points appropriés sont les GC_UNBIND et GC_UNLOOP après lequel sont ajoutés, par le compilateur CLAIRE, un appel à la (nouvelle) macro POP_SIGNAL : #define POP_SIGNAL {if (n_pending_signal > 0) \ kernel_pop_signal();} Une fois encore, seule la plateforme UNIX supporte les signaux, un signal sous win32 termine l'exécution du processus. * Référencement des OID à l'adresse nulle Dans le noyau XL les OID ne sont pas référencés par rapport à l'adresse de Cmemory mais par rapport à l'adresse nulle ce qui est bien plus performant car on économise l'indirection dynamique de Cmemory nécessaire à une conversion OID/adresse. Macro de conversion <ycs> : #define OBJECT(A,B) \ ((class A*)&Cmemory[(B & ADR_MASK) + 1]) #define _oid_(A) \ (OBJ_CODE + (((int)A - (int)&Cmemory[0]) >> 2) - 1) Macro de conversion XL : #define OBJECT(A,B) ((class A *)((unsigned)B << 2)) #define _oid_(A) (OBJ_CODE | ((unsigned)A >> 2)) Ce n'est qu'un jeu d'écriture mais les gains de performances observé sont de l'ordre de 10 à 20%. Ces gains sont plus important encore dans le cas ou plusieurs processus sont simultanément en cour d'exécution. * Ligne de commande Le processus d'initialisation de XL CLAIRE est le suivant : • • • • allocation de Cmemory metaLoad call_main lecture des options de la ligne de commande Call_main est rendu obsolète par la gestion des options de la ligne de commande introduite dans XL CLAIRE mais reste présente pour la compatibilité avec <ycs>. XL CLAIRE dispose d'un moteur de parsing des options de la ligne de commande, chaque module peut définir des handlers répondant à une option donnée. Dans la distribution XL CLAIRE le fichier ccmain.cl à été éclaté afin que chaque option supportée soit prise en charge par le module approprié. Par exemple l'option -cm est prise en charge par le module Generate et l'option -f est prise en charge par le module Reader : [option_parsed() : void -> // appelé une seule fois pour chaque module qui définit // cette restriction et une fois que toutes les options // ont été parsées none] [option_usage(opt:{"-f", "-ef"}) : tuple(string, string, string) -> tuple("Load file", "{-f | -ef} <file:path>", "Load the CLAIRE file <file>. The given path may contain an extension assuming .cl by default. When the <-f> option is used, the file is assumed to contain CLAIRE definitions (variables, class, methods) wheheas <-ef> attemps to read a file made of CLAIRE expression.")] [option_respond(opt:{"-f", "-ef"}, l:list) : void -> if not(l) invalid_option_argument(), let path := l[1] in (l << 1, case opt ({"-f"} load(path), {"-ef"} eload(path)))] Ainsi, dès qu'une application est compilée avec le module Reader l'option -f est implicitement supportée. L'option -h est supportée par le module Core et se sert des restrictions d'option_usage pour dresser une aide accessible à la ligne de commande. Ainsi on peut demander l'aide de l'option -f: Pasted Graphic 14.tiff ¨ * Interprète et complétion automatique Dans XL CLAIRE l'invite de commande est préfixée du PID du process. Cela permet notamment de faire des fork à l'interprète et de savoir vers quel processus on envoie les commandes. Si le système sur lequel XL CLAIRE est installé dispose de la librairie editline alors l'interprète peut en tirer parti pour proposer une interface avec un historique des commandes ainsi que la complétion automatique de symboles connus par claire avec la touche TAB. De plus, l'interprète est capable de lister les restrictions d'une propriété lorsque l'on tape TAB après la parenthèse d'un appel fonctionnel : [18386]claire> princ([TAB] princ(string) -> void princ(string, integer, integer) -> void princ(symbol) -> void princ(integer) -> void princ(float) -> void princ(char) -> void princ(bag) -> void [18386]claire> princ( Enfin, si on tape TAB alors que la ligne de commande est vide on permute entre un shell système et l'interprète CLAIRE sans quitter CLAIRE . * Sampling Cmemory Un lot de bug concernant le GC ont étés levé en 2004, dans ce contexte XL a développé des outils pour tracer l'évolution de Cmemory en cours d'exécution. L'option « -sample <period> » permet d'activer le trace de Cmemory avec une périodicité de <period> millisecondes. A l'issue de l'exécution on peut utiliser le fichier texte généré clmemory.sample avec une application type Excel ou gnuplot pour produire un graphique de l'évolution de Cmemory au cours du temps. Cette opération ne peut être effectuée que sous UNIX car l'API fork est utilisée. Chaque échantillon est généré depuis un processus fils qui effectue un GC dans lequel les objets de Cmemory sont comptabilisés en différentes sortes. Les processus fils sont crées de façon asynchrone par une alarme levée toutes les <period> millisecondes. Ainsi le processus père peut continuer sont exécution pendant que le fils produit son échantillon et l'activation de l'échantillonnage ne perturbe pas l'exécution du processus sur une machine multiprocesseur. Par exemple, le graphique ci-dessus à été réalisé sur une compilation des métas de CLAIRE en « -s 3 3 » avec une période de 10 millisecondes : Pasted Graphic 8.tiff ¨ Le temps d'exécution en abscisse est exprimé en seconde et l'occupation mémoire en ordonnée est exprimé en méga-octet. On distingue les real short des short (respectivement real chunk et chunk), le premier comptabilise tous les objets de la mémoire short qui sont pointés par une racine tandis que le deuxième comptabilise tous les objets y compris ceux qui ne sont plus pointés et qu'un prochain GC devrait détruire. Ainsi on vérifie à tout instant l'identité suivante : (real short) + (real chunk) = (object) + (string) + (symbol) + (bag) + (array) Le graphique montre que deux GC se sont produits, après un GC ont vérifie idéalement l'identité suivante : (chunk) = (real chunk) (short) = (real short) Le graphique montre également une augmentation régulière du nombre de bag/string/objet qui s'interprète par le nombre croissant d'objets instanciés par le Reader lors de la lecture des fichiers méta. On voit enfin l'évolution des objets temporaire crées lors de compilation des métas sur les deux courbes chunk et short. * pré-allocation des listes annulables Depuis CLAIRE 3 les listes annulables doivent être allouées une fois pour toute, cela rend le mécanisme de changement de monde plus performant car seule les adresses (directement sur le contenu de liste), où s'effectue les « updates », ont besoin d'être retenues par les piles de monde. Par contre, il reste fastidieux de créer de telles listes vide par défaut sans bricolage. Par exemple : lesson <: ephemeral_object( students:list[student] = make_list(100, student, default_student)) (store(students)) lesson!() : lesson -> let l := lesson() in (shrink(l.students, 0), l) Dans cet exemple if faut prévoir une instance par défaut (default_student) pour initialiser la liste puis faire un shrink car on désire une liste vide à l'initialisation, en plus on alloue inutilement une liste pour le prototype de la classe. Pour simplifier la création de ces listes, les bags ont un champ prealloc, dans le noyau XL, qui modifie la politique de copie du bag : class bag: public ClaireType { public: int length; ClaireType *of; OID* content; int prealloc; ... }; bag *copy_bag(bag *l) { ... obj->prealloc = l->prealloc; int m = l->length; if (obj->prealloc > m) m = obj->prealloc; if (ClAlloc->statusGC != 2) GC_PUSH(obj); OID *x = ClAlloc->makeContent(m); ... } Deux méthodes relatives ont étés ajoutées pour la création de liste « pré-allouées » : prealloc_list(t:type, len:integer) -> list prealloc_set(t:type, len:integer) -> set Ces deux méthodes renvoient des bags vides mais avec le champ prealloc initialisé à len. Elles sont particulièrement utiles pour construire le prototype d'une classe qui contient des slots de type « liste storé ». La liste allouée pour le prototype ne prendra pas de place en mémoire, c'est seulement lors de l'instantiation, qui fait implicitement appel à la copie de liste, qu'un contenu sera effectivement alloué. Ainsi l'exemple ci-dessus peut être réécrit comme suit : lesson <: ephemeral_object( students:list[student] = prealloc_list(student, 100)) (store(students)) lesson!() : lesson -> lesson() Note : Avec le champ prealloc, une instance de bag a une taille de 4*sizeof(int) et peut toujours tenir dans une cellule de la zone mémoire short object. Autrement dit, cette feature ne nécessite pas de mémoire supplémentaire. * printf @ port Avec XL CLAIRE on peut utiliser la construction printf avec comme premier argument un port. Ce qui allège certaines écritures ou l'on souhaite se passer d'une re-direction avec use_as_output, par exemple : printf(ctrace(), "Hello\n") qui est équivalent à : let p := use_as_output(ctrace()) in (printf("Hello\n"), use_as_output(p)) * Toplevel et debugger Dans XL CLAIRE la toplevel à été réécrite en CLAIRE et insérée dans le module Reader. Le debugger CLAIRE à été amélioré, inspiré par gdb, ce nouveau debugger est encore à l'état de prototype et demande un recettage approfondi (voir le paragraphe roadmap). * Ports Les ports XL CLAIRE ont subit une première évolution. Ils sont organisés en une hiérarchie d'objet dérivés de freeable_object et ne sont plus des imports. Ainsi, on assure que tous les ports sont correctement fermé à l'issue de l'exécution par l'implémentation de free! @ port. L'API de port <ycs> est toujours supportée : • • • • putc(char, port) -> void getc(port) -> char flush(port) -> void fclose(port) -> void Des API proches de celles de la lib C ont étés ajoutées : • • • • • • • eof?(port) -> boolean fread(port, string) -> integer fread(port, integer) -> string fwrite(string, port) -> void freadwrite(port, port) ->integer freadline(port, string) -> string freadline(port, subtype[string]) -> tuple(string, string) Ces API en combinaison avec la nouvelle représentation de string permet de manipuler facilement des flux binaires. La méthode freadwrite permet de transmettre un flux, la famille de méthode freadline (dont deux restrictions seulement sont mentionnée ci-dessus) permet de « lire jusqu'à », par exemple : explode_words(self:port) : list[string] -> let words := list<string>() in (while not(eof?(self)) let (w, blank) := freadline(self, {"\n", "\r", " ", "\t"}) in words :add w, words) Note: L'implémentation des ports est en cours de réécriture tout en CLAIRE (voir le paragraphe roadmap). * Tables Dans la version XL CLAIRE les tables, lorsqu'elles ne sont pas nommées (instanciées avec make_table), peuvent être détruites par le GC. Ainsi on peut avoir des tables temporaires. * Terminal et couleurs Le noyau XL supporte les couleurs pour la sortie vers le terminal (et en HTML comme on le vera plus tard). Les couleurs sont sélectionnées par des séquences d'échappement envoyée au terminal. Pour envoyer une chaîne colorée vers le terminal il faut utiliser la méthode color_princ qui sait interpréter des séquences d'échappement commençant par un backquote (comme `RED), un double backquote active le style gras. Pour que les couleurs apparaissent à l'écran il faut spécifier l'option -color sur la ligne de commande, par exemple : Pasted Graphic 26.tiff ¨ La construction printf utilise la méthode color_princ pour les parties statiques de la chaîne de formatage et alterne les couleur dès qu'une partie dynamique (~S/~A/~I) est rencontrée. Les messages d'erreur, qui sont affichés en rouge, deviennent ainsi beaucoup plus lisibles : Pasted Graphic 27.tiff ¨ * Script UNIX Avec XL CLAIRE on peut écrire des script UNIX. Un script UNIX est un fichier texte ayant des droits d'exécution et dans lequel est spécifié sur la première ligne un interpréteur (CLAIRE dans notre cas). Le Reader à été adapté pour ignorer une telle ligne si elle est présente. Voici un exemple de script : #!/usr/local/bin/claire -x (printf("Hello world\n")) D'après la spec UNIX on ne peut spécifier qu'une seule option sur cette première ligne. La famille d'option -x est prévue à cet effet : Pasted Graphic 28.tiff ¨ Note 1: L'option -nologo sert à ne pas afficher le message d'initialisation de CLAIRE ni le « bye... » final. L'option -q sert à quitter CLAIRE sans lancer d'interpréteur. Note 2: Le noyau intercepte l'option -x<S>-<W> avant l'initialisation de CLAIRE afin de lancer claire avec les paramètres mémoire spécifiés. L'environnement de production Eclaireur est un IDE pour CLAIRE qui offre une gestion de projets et une gestion de la compilation. Eclaireur tourne exclusivement sous win32 et ne permet pas de créer et compiler aisément des applications pour la plateforme UNIX. Sous UNIX on préférera utiliser la ligne de commande pour compiler ses modules. XL CLAIRE a été étoffé d'un ensemble d'options de ligne de commande qui facilitent la création, la compilation et le partage de modules. * Installation et architecture Afin de diminuer les incompatibilités que l'on peut rencontrer d'un UNIX à l'autre le processus d'installation d'XL CLAIRE commence par le lancement d'un script configure chargé de lever ces incompatibilités. Durant cette phase de configuration on définit également une variable identifiant l'OS (darwin, linux, sun...), l'architecture matérielle (ppc, i386) et le compilateur C++ (cc, gcc) ainsi que la version de ce compilateur que l'on utilise pour mettre à jour le slot compiler.env, comme par exemple : Linux-i686-g++3.3.3 Darwin-ppc-g++4.0.0 Le processus d'installation est le suivant, on se place dans le dossier des sources d'XL CLAIRE puis : ./configure [--prefix=<dossier d'installation>] make [sudo] make install On peut spécifier un dossier d'installation particulier, se dossier est « /usr/local » par défault. A l'issue de l'installation on se retrouve avec un dossier claire crée au path d'installation qui contient: Pasted Graphic 31.tiff ¨ Ainsi on peut avoir plusieurs versions de claire, pour plusieurs architectures et pour différents compilateurs C++, installées dans le même dossier. Le dossier lib est réservé aux publications de module (voir le paragraphe partage de module). * Création de nouveaux modules : option -nm L'option -nm « new module » permet de créer facilement un nouveau module. L'aide en ligne associée est la suivante : Pasted Graphic 18.tiff ¨ Le module ainsi crée est vide (aucune définition) mais peut être compilé directement. Si, plus tard, on ajoute des fichiers sources au module il faudra éditer le fichier init.cl. Le fichier init.cl généré contient la définition du module, son load, ainsi qu'en commentaire toutes les configurations possibles du compilateur ce qui évite de perdre du temps à se souvenir de telle ou telle option (voir plus bas). Par exemple, pour créer un module myModule utilisant Core et fait de model.cl on fait: Pasted Graphic 16.tiff ¨ Le fichier init.cl généré est le suivant : // init file for module myModule // created Thu Aug 18 10:33:36 2005 by claire v3.3.35 (use_module("Core")) myModule :: module( uses = list(Core), made_of = list("model.cl"), source = "source", version = "v1.0.0") // put your version here (load(myModule)) // Here you can customize the C++ compiler. // You can uncomment and set any of the following option : // ==== external libraries needed at link time ==== ;(compiler.libraries :add "-lsome_lib") // ==== C++ compiler options ==== ;(compiler.options[1] :/+ "-a_cpp_option") // Optimize mode (-O) ;(compiler.options[2] :/+ "-a_cpp_option") // Debug mode (-D) ;(compiler.options[3] :/+ "-a_cpp_option") // Release mode // Here you can customize the CLAIRE compiler. // You can uncomment and set any of the following option : // ==== compiler safety ==== // 0 -> super safe // 1 -> safe // 2 -> trust explicit types & super // 3 -> no overflow checking // 4 -> assumes no selector or range error // 5 -> assume no type errors of any kind ;(compiler.safety := 1) // ==== compiler naming convention ==== // 0 -> long & explicit names // 1 -> shorter names // 2 -> protected names ;(compiler.naming := 0) // ==== compiler inline flag ==== // set it to true if you want to include inline definitions in the generated library ;(compiler.inline? := false) // ==== compiler overflow flag ==== // set it to true to produce safe code with respect to owerflow ;(compiler.overflow? := false) // ==== fast dispatch flag ==== ;(FCALLSTINKS := false) // Here you can customize the CLAIRE code generator. // Some symbol may be reserved in the target language, // for such symbol you have to define a translation : ;(Generate/C++PRODUCER.Generate/bad_names :add some_symbol) ;(Generate/C++PRODUCER.Generate/good_names :add symbol!("some_symbol_translated")) Note : La commande use_module est décrite plus bas au sujet du partage de module. * Compilation de module : option -cm L'option -cm « compile module » permet de compiler un module en un exécutable. L'aide en ligne associée est la suivante : Pasted Graphic 13.tiff ¨ Contrairement à la version <ycs> on peut omettre le module lorsque l'on utilise l'option -cm depuis le répertoire d'un module. Des options ont été ajoutées pour customizer la compilation rapidement: Pasted Graphic 21.tiff ¨ Par exemple pour compiler le module myModule en -O2 rapidement, on utilisera : claire -cpp -O2 -cm * Partage de module : options -publish et -export Les options -publish et -export permettent de partager un module. Par cette opération une copie du module (version, sources, init.cl, header et librairie) est effectuée vers un dossier connu par CLAIRE. CLAIRE sait ainsi retrouver la liste des modules partagés, cette liste est utilisée par la commande use_module que l'on place dans un fichier init.cl avant une définition de module afin que tous les modules utilisés (slot uses) soient connus. L'aide en ligne pour ces options est: Pasted Graphic 20.tiff ¨ Ces deux options ne peuvent être utilisées que depuis le répertoire d'un module sur lequel elles s'appliquent. On distingue les publications, placées dans le répertoire de la version de CLAIRE installée, des exports qui prennent place dans un dossier définit par l'utilisateur via la variable d'environnement CLAIRE_LIBS. Ainsi on peut avoir une version de CLAIRE installée dans un dossier partagé avec des publications de modules généraux et utilisables par plusieurs utilisateurs et des modules locaux accessibles par un utilisateur en particulier. On peut publier plusieurs versions d'un même module pour une architecture donnée, par exemple voici la structure du module Casper une fois publié (dans le dossier d'installation de claire): Pasted Graphic 32.tiff ¨ Dans le cas d'un export la structure de dossier est identique. Ainsi pour chaque version publiée d'un module on retrouve le init.cl et les sources de la version publiée ainsi que la librairie et le (ou les) .h du module pour un couple « version de CLAIRE - architecture » donné. Connaissant cette architecture, la commande use_module sait énumérer le dossier lib (ou un dossier définit dans la variable d'environnement CLAIRE_LIBS) pour retrouver un module donné. * Compilation de librairie : options -cls et -call L'option -cls permet de compiler la librairie d'un module (on doit se trouver dans le dossier du module) et l'option -call permet de compiler un ensemble de module (on doit se trouver dans un dossier parent d'un ou plusieurs modules). L'aide en ligne de ces options est: Pasted Graphic 22.tiff ¨ Ces options peuvent être combinées avec les options de publication, par exemple la commande suivante va recompiler toute les librairies release (-call) des modules qui se trouve dans des sous dossiers du dossier courant en safety 5 (-os 5) avec l'option d'optimisation C++ -O2 (-cpp -O2). Chaque module recompilé sera ensuite publié (-publish) avec les droits root (-sudo) et éventuellement écrasera (-ov) des publication existantes: claire -os 5 -cpp -O2 -call -sudo -ov -publish Lorsque l'option -call est utilisée et que les modules à compiler sont interdépendants les sources des versions locales sont préférée aux versions éventuellement déjà publiées. * Informations de partage: option -ml et -mi L'aide de ces options est la suivante : Pasted Graphic 25.tiff ¨ L'option -ml « module list » donne une liste des modules partagés. La commande use_module évoquée plus haut ne pourra trouver qu'un module de cette liste. A titre d'exemple voici la liste des modules publiés sur mon système à l'heure de l'écriture de ce document : Pasted Graphic 23.tiff ¨ Sur chaque ligne se trouve le nom d'un module partagé et l'ensemble des versions disponibles pour ce module. Pour chaque version se trouve entre crochets les lettre r et/ou d qui indique la présence d'une version release et/ou debug. Enfin on peut obtenir des informations sur un module en particulier avec l'option -mi « module information », par exemple: Pasted Graphic 24.tiff ¨ L'infrastructure Web * Le module Wcl Après avoir développé certains projets à l'aide du langage PHP, XL s'en est inspiré et à conçu sa propre infrastructure pour la génération de page dynamique. Le module Wcl « Web CLAIRE » est au coeur de cette infrastructure. A l'origine Wcl est un interpréteur de balises de code CLAIRE embarqué dans une source HTML. Aujourd'hui Wcl rempli plusieurs tâches supplémentaires dont la prise en charge des protocoles HTTP et CGI, la lecture des données de formulaires HTML et la sauvegarde des données de session utilisateur dans un format binaire à l'aide du module Serialize. Ci-dessous on a un schéma de principe concernant le rôle du module Wcl : Pasted Graphic 29.tiff ¨ Un agent CLAIRE équipé du module Wcl sait communiquer avec le serveur web populaire Apache 2 par l'intermédiaire du module apache mod_wcl développé par XL. Ce dernier est chargé, lors d'une connexion au serveur, de lancer l'agent CLAIRE dans un environnement type CGI et de lire la sortie de l'agent qui contient à la réponse à la requête (par re-direction des entrée/sortie). Pour cela il est important, comme mentionné plus haut, que la sortie de l'agent ne soie pas polluée par des traces de GC par exemple. Les traces sont redirigés vers un fichier et peuvent être visualisés à l'aide, par exemple, de la commande UNIX tail. Le module mod_wcl lance un agent CLAIRE avec l'option -wcl qui, lorsqu'elle est prise en charge (option_respond @ {"-wcl"}), décode l'environnement CGI pour trouver et loader le fichier requis. Avant de charger effectivement le fichier wcl requis le moteur Wcl remplit une table nommée $ avec les variables de formulaire décodées depuis stdin et l'environnement: claire/$[key:string] : any := false * fichiers .wcl On appelle fichier wcl un fichier HTML qui contient des balises de code CLAIRE. Par exemple : <html> <? (printf("Ce message est généré par CLAIRE")) ?> </html> Une fois passer dans la moulinette wcl le fichier devient : <html> Ce message est généré par CLAIRE </html> La balise <? introduit donc du code CLAIRE, deux balises supplémentaires sont disponibles pour introduire du code: <?= et <?== qui, non seulement, évalue l'expression qui suit mais invoque également une méthode de print (respectivement echo et self_html) sur le résultat de l'évaluation. Par exemple: <html> 1 + 2 = <?= (1 + 2) ?> </html> qui produit le résultat : <html> 1 + 2 = 3 </html> L'interpréteur Wcl introduit est en fait une nouvelle facilité de print. En effet (dans un fichier claire classique) on peut introduire une séquence wcl par la balise ?>, et on en fait largement usage avec les modules Pdf ou Mail (voir plus bas). * session utilisateur Les sessions utilisateur contiennent des données temporaires qui permettent un suivi entre deux connexions successives. On utilise la méthode session_start ou session_url pour démarrer une session utilisateur depuis un fichier wcl. L'identifiant de session peut être transmit sous forme de cookie (session_start) ou directement sur l'URL (session_url). A une session est associé un fichier sérialisé contenant les variables qui auront étés mises en session grâce aux API register et rregister : register(var:string, val:any) -> any rregister(var:string, val:any) -> any Les variables mises en session sont ensuite accessibles via la table $. rregister est une variante de register qui sérialise l'arbre d'objets inter-pointés via val jusqu'aux objets nommés. * Erreurs en Wcl Les erreurs générées par un srcipt wcl ne sont pas fatales, mais seulement affichées dans la syntaxe HTML. La gestion des couleurs évoquée plus haut n'est plus basée sur des séquences d'échappement mais sur des balises HTML. Les erreurs sont également rappelées dans le fichier de trace. * Un binaire pour un site web! Les fichiers wcl peuvent être compilés en C++. Ainsi on peut créer un binaire qui contient tout un site web. Les performances en terme de temps de réponse sont fantastiques comparé à d'autre technologie comme PHP. * Infrastructure de modules XL a développé des modules utilitaires pour réaliser différentes tâches répétitives comme le dialogue avec une base de donnée, la génération de document PDF ou encore l'envoie d'email. Cette infrastructure logicielle peut être schématisée comme suit: Pasted Graphic 1.tiff ¨ Serialize: Permet de stocker un arbre d'objets (y compris les relations inter-objets) dans une forme binaire. Un flux serialisé peut par exemple être transmis sur un port réseaux pour transmettre des instances CLAIRE à un processus distant. On utilise Serialize pour stocker en fichier les données de session. Locale: Implémente la méthode translate pour les applications multilingues et gère un dictionnaire de termes. Zlib: Implémente un port dédié à la compression/décompression d'un flux. Basé sur la librairie populaire zlib. Soap: Permet de faire des appels distants à des procédures (client) avec le protocole SOAP ainsi que de créer un service SOAP (serveur). WclSite: Couche d'abstraction pour l'écriture de site web. Administration d'application internet et gestion de menu, de groupe, d'utilisateur, d'authentification et de droit d'accès. Pdf: Lecture et écriture de document PDF éventuellement compressés. Basé sur un convertisseur HTML vers PDF pour une écriture concise de documents complexes (paragraphe, titres, tables, image, listes, saut de page automatiques...). Supporte la signature digitale par certificat X509 en écriture ainsi que la validation d'une signature en lecture. C'est un outil puissant quand il est utilisé avec la syntaxe Wcl, par exemple: doc :: Pdf/document!("A4") (Pdf/print_in_html(doc) ?> <p align=center> Hello world, by CLAIRE v<?== release() ?> </p> <? Pdf/end_of_html(doc)) (Pdf/print_in_file(doc, "helloworld.pdf")) Mail: Lecture, écriture et envoie de message électronique via le protocole SMTP. Comme pour Pdf on profitera largement de la syntaxe Wcl pour écrire le corps d'un message HTML. Db: Couche d'abstraction pour la communication avec un SGBD. Permet de soumettre des requêtes SQL et d'en lire les résultats. Mysql & Postgres: Driver de base de donnée, ces deux modules implémentent l'interface définie dans le module Db. Dbo: « database objects », prend en charge le mapping entre une classe CLAIRE et une table en base de donnée. Dbo permet de communiquer avec une base de donnée sans écriture la moindre requête SQL. Openssl: remonte en CLAIRE des fonctionnalités de la librairie OpenSSL (gestion de certificats, gestion de clefs publique/privée, chiffrement...). Pdf utilise Openssl pour traiter les signatures digitales. Roadmap * Septembre 2005 Les ports sont en cours de réécriture tout en CLAIRE. Avec les nouveaux ports, on pourra écrire ses propres ports en CLAIRE. Les nouveaux ports CLAIRE définissent deux classes de base : claire/port <: freeable_object( firstc:integer = 32, closed?:boolean = false) claire/device <: port(filters:list[filter]) claire/filter <: port(dev:device, target:port) (inverse(dev) := filters) Les notions de périphérique et de filtre sont introduites: • un filtre (filter) est un port qui peut transformer/collecter/analyser un flux • un périphérique (device) correspond à un port physique (fichier, socket ...), on peut lire et écrire sur un périphérique à travers une chaîne de filtres. Allié au string du noyau XL CLAIRE, cette nouvelle implémentation permet de prendre charge facilement des flux encodés par différents protocoles imbriqués. XL possède déjà un prototype prometteur entièrement compatible avec l'API existante. Cette nouvelle implémentation fait largement usage du fast dispatch (introduit dans CLAIRE 3) qui s'avère bien plus efficace que les appels virtuels C++ qui sont aujourd'hui utilisés. De plus, sont implémentés en CLAIRE, des filtres qui remplacent l'interface C FILE*(buffering par ligne et par block) ainsi que différents services comme compter les lignes lues par le reader. Sur la compilation des métas et du compilateur, qui fait beaucoup de lecture/écriture de fichier on observe un gain de 40% sur le temps de compilation! * Septembre 2005 Dans le noyau, il faut changer le champ ascii de la classe ClaireChar (interface C++ de char) en unsigned char afin de lever les ambiguïtés introduites avec un char signé. La table de caractère (ClRes->ascii) sera ainsi parfaitement définie sur l'intervalle 0, 255 (aujourd'hui elle en contient 512). * Septembre 2005 Lever la limitation MAXBUF en changeant ClEnv->buffer en un tableau dynamique réallouable. Le buffer ne sera plus dans la mémoire CLAIRE. * octobre 2006 Création d'un module dédié aux API de la libC ajoutées historiquement au noyau. Ceci afin de diminuer la taille du noyau. * d'ici 2006 Wcl va être « explosé » en trois entités différentes: • La partie purement liée à la syntaxe Wcl va être descendue au niveau du Reader. On pourra utiliser cette syntaxe comme une alternative à printf sans l'usage d'un quelconque module (lève la dépendance inutile à Wcl pour les modules Pdf et Mail). • La partie chargée de des protocoles CGI et HTTP va faire partie d'un module à part: Http. Ce module sera utilisé par le module Soap et le module WclSite qui font aujourd'hui usage de Wcl. • La partie qui prend en compte les sessions va être intégrée au module WclSite. * d'ici 2006 Finition, recettage du deboggeur. * fin 2005 - 2006 Implémentation de la construction scanf. scanf est à la lecture ce que printf est à l'écriture. * 2006 Mise à jour du site claire-language.com. Le site claire-language utilise déjà l'infrastructure web et sera mis à jour avec les évolutions des modules Mail, Wcl et WclSite.