Il est né ?
Vous êtes ici : xgarreau.org >> aide >> devel >> tcl : Critcl, extensions TCL en C à la volée
Version imprimable

Critcl, extensions TCL en C à la volée

Critcl signifie Compiled Runtime In Tcl et a été écrit par Jean Claude Wippler, que les utilisateurs de tclkit et autres starkit connaissent déjà. C'est bien mais ça ne nous dit pas à quoi ça sert.

Critcl permet simplement d'écrire du C dans du Tcl, le code C étant compilé et chargé lorsqu'on en a besoin. Afin de ne pas avoir à supporter le temps de compilation à chaque lancement du script, Critcl utilise un système de cache, basé sur la somme MD5 des sources C générées pour ne pas avoir à les recompiler à chaque invocation des procédures ainsi fournies.

Pour pouvoir distribuer les scripts Tcl utilisant critcl sur des machines ne disposant pas de compilateur C, Critcl permet également de créer des extensions Tcl natives sans devoir installer les sources de Tcl. Ceux dans l'assistance qui ont déja dû recompiler une extension Tcl "classique" apprécieront la beauté du geste.

Alors, pourquoi écrire du C dans Tcl ? La plupart des utilisateurs de Tcl sont justement des gens qui fuient plus ou moins le C et autres langages compilés. Tout simplement parce qu'il y a des choses qu'on ne peut pas faire "juste" en Tcl ou pour des raisons de vitesse. il faut bien le reconnaître, le Tcl est tout de même plus connu pour sa concision que pour ses performances. Un autre "avantage" est de pouvoir "cacher" le code critique puisque vous pourrez ne distribuer que la version compilée du code C. Tout le monde ne fait pas de l'open source et le mélange C/Tcl offre cet autre avantage non négligeable pour ceux qui tiennent à garder en partie leur savoir faire caché poru des raisons qui leur appartiennent (ne lançon pas un troll dès l'introduction) ...

Installation

Il nous faut tout d'abord télécharger tclkit pour linux (http://www.equi4.com/pub/tk/8.4.11/tclkit-linux-x86.gz). Il s'agit d'un unique fichier binaire contenant un interpréteur tcl/tk qui s'avère très pratique. Lancé seul, il offre une console semblable à wish, utile pour des démonstrations ou développements rapides (on lui préfèrera toutefois tkcon, la Tk Advanced Console pour une utilisation au quotidien).

Une fois téléchargé, il nous faut le décompresser, renommer, rendre exécutable et copier quelque part où on puisse le trouver (/usr/bin peut être un bon choix mais tout autre répertoire du PATH fera l'affaire)

gzip -d tclkit-*.gz
mv tclkit-* tclkit
chmod +x tclkit
mv tclkit /usr/bin/

On peut ensuite, pour des raisons pratiques, télécharger SDX, le Starkit Developer eXtension. (http://www.equi4.com/pub/sk/sdx.kit) Ce dernier nous permettra de "packager" et "dépackager" les starkits.

Il nous reste ensuite à télécharger critcl.kit (http://www.equi4.com/pub/sk/critcl.kit) et tester le tout en tapant dans une console:

$ tclkit critcl.kit 

        To compile and run a tcl script
            critcl [-force] [-keep] file[.tcl]

        To compile and build a shared library or package
            critcl [-force] [-keep] [-lib|-pkg] name [infiles...]

        -force      force compilation (ignores cache)
        -keep       keep intermediate C files
        -lib        generate $name.so
        -pkg        generate a package structure under lib/$name

        all options may be abbreviated to the first character

Cet affichage vous sera vraisemblablement présenté dans une fenêtre Tk à part sauf si vous n'utilisez pas de serveur X à ce moment là.

On va maintenant ajouter tout ce qui est fourni par critcl à notre environnement tcl. On commence par "unwrapper" le tout.

$ tclkit sdx.kit unwrap critcl.kit
48 updates applied

On obtient un répertoire critcl.vfs. Ce dernier contient un sous répertoire lib dont on va copier le contenu là où Tcl saura le trouver, c'est à dire dans un des répertoires renvoyés par la commande Tcl set auto_path.

Nota Bene: On peut optionellement garder le contenu là où il est mais il faudra commencer tout nos scripts pendant le développement de notre extension par une commande semblable à celle donnée ci dessous, qui ajoute le répertoire lib du kit dans notre répertoire de travail Tcl.

lappend auto_path ./critcl.vfs/lib

En travaillant avec tclkit, on peut s'affranchir de cette étape en "sourçant" le kit, comme on le verra par la suite mais il est important de savoir que cette solution existe.

Test

Il convient de vérifier le bon déroulement de l'installation. Pour celà on lance tclsh, tclkit ou tkcon et au prompt de l'interpréteur on tape les lignes suivantes (éventuellement après s'être placé dans le bon répertoire)

% lappend auto_path ./critcl.vfs/lib
/usr/bin/tclkit/lib/tcl8.4 /usr/bin/tclkit/lib ./critcl.vfs/lib
% package require critcl
0.34

Si vous obtenez un résultat semblable, l'installation s'est déroulée correctement.

Utilisation

Dans les exemples suivants, je partirai du principe que la modification d'auto_path n'est pas nécessaire (c'est à dire que vous avez tranférer le contenu de critcl.vfs/lib "là où il faut" ou que vous êtes débrouillés par d'autres moyens).

Une somme

On va commencer par créer une procédure pour faire des sommes, quelque chose de simple pour commencer. Créons donc un script somme.tcl contenant ceci:

package require critcl
::critcl::cproc somme {int n1 int n2} int {
	return (n1 + n2);
}

On rencontre ici ::critcl::cproc, qui permet de définir nos procédures à partir de code C.

Les arguments sont le nom de la procédure, la liste des arguments précédés de leur type (pas de virgules ici, et le tout entre accolades, amis du C attention aux habitudes, c'est encore du Tcl que l'on tape sur cette ligne), le type retourné et enfin, le code C.

Dans l'interpréteur, après avoir "sourcé" ce script, on peut utiliser la procédure somme.

% source somme.tcl
% set neuf [somme 4 5]
9
% somme
wrong # args: should be "somme n1 n2"
% somme A 4
expected integer but got "A"

Les messages d'erreur que l'on peut observer ci-dessus sont générés par Tcl et critcl automatiquement, à partir de la définition de votre cproc. Appréciez le temps que vous venez de gagner.

On peut ajouter ainsi les trois autres opérations basiques. On se rend alors compte, lors de la première utilisation d'une des procédures du temps de compilation mais aussi et surtout que ce temps est inexistant ensuite. Et celà est d'autant moins grave que, nous le verrons par la suite, c'est un package que l'on utilisera et distribuera au terme de la phase de développement, c'est à dire une extension déjà compilée.

Critcl et GD

Maintenant que nous avons vu une utilisation basique de critcl, on va s'intéresser à un exemple plus utile.

On va créer une procédure permettant de générer une image contenant un texte écrit dans une taille donnée, avec une police donnée. Et pour atteindre ce but, on va utiliser la librairie gd (avec le support fontconfig).

On aurait tous les éléments nécessaires pour débuter. Malheureusement, la librairie gd couramment fournie par les distributions linux n'inclut pas le support fontconfig. Nous allons donc compiler notre propre librairie gd, que nous incluerons en statique à notre extension, pour qu'elle soit aisément distribuable. Nous allons compiler et installer notre gd dans ~/myprefix/lib et ses headers dans le répertoire ~/myprefix/include.

Pourquoi ainsi se prendre la tête et d'abord, fontconfig, à quoi ça sert ? Par défaut, quand on veut utiliser les fonctions de rendu de texte de gd avec des polices Truetype, on doit leur passer en argument un chemin vers un fichier de police. Comme on ne sait pas à l'avance quelles polices sont disponibles, on utilise, depuis que c'est disponible, fontconfig. Cette librairie utilise un cache de polices pour convertir des chaînes telles que FreeSans:bold:italic en l'emplacement de la police la plus proche. Et gd utilise alors le résultat fourni pour trouver la police correspondante et faire son rendu.

Quoiqu'il en soit, ce n'est pas si compliqué et je m'en vais vous le prouver. Si toutefois, vous ne vouliez pas vous encombrer de ce qui peut vous sembler un détail, il vous suffira d'utiliser gd de façon classique. C'est à dire, définir une variable d'environnement GDFONTPATH (ou modifier la valeur de DEFAULT_FONTPATH dans les sources de gd) pointant vers un répertoire contenant vos polices et d'utiliser en argument des fonctions concernées le nom d'un des fichiers s'y trouvant (vous allez aimer fontconfig, je vous assure, lisez la suite).

Notre gd

Il faut commencer par vérifier que nous avons bien tous les éléments nécessaires de la chaîne de compilation (configure, gcc, make) et les headers des bibliothèques nécessaires. A titre d'information, sur une debian, voici les paquets nécessaires (hors compilateurs et accessoires):

Il nous faut récupérer les dernières sources de gd (http://www.boutell.com/gd/) puis les configurer, compiler et installer.

$ tar xvfz gd-2.0.33.tar.gz
$ cd gd-2.0.33
$ ./configure --prefix=${HOME}/myprefix --without-x
...
   Support for PNG library:          yes
   Support for JPEG library:         yes
   Support for Freetype 2.x library: yes
   Support for Fontconfig library:   yes
   Support for Xpm library:          no
   Support for pthreads:             yes

$ make
$ make install

Le --without-x évite le conflit avec une éventuelle librairie freetype installée dans /usr/X11R6/lib comme c'était malheureusement le cas sur une des machines qui ont servi à la rédaction de cet article. En contrepartie, on perd le support Xpm. Il y a d'autres façons de contourner le problème et peut être ne le rencontrerez vous pas. Mais c'est le choix que j'ai fait et je vous le donne au cas où.

L'important pour la suite est que votre gd ait bien le support des librairies Fontconfig et Freetype.

Notez que vous n'avez pas besoin d'être root pour la phase make install puisque vous faites une installation dans votre répertoire utilisateur. critcl ayant en revanche du mal à se rappeler des options qu'on lui passe, vous aurez tout intérêt à utiliser la commande suivante pour lui spécifier ou trouver les headers.

export CPATH=${HOME}/myprefix/include/

Retour à critcl

On doit commencer par charger le package critcl

package require critcl
...

Ensuite, on doit ajouter des options au linker pour que ce dernier puisse trouver les librairies nécessaires. On utilise pour cela la comme ::critcl::clibraries qui passe au linker les options qui suivent, sans modification.

::critcl::clibraries -lpng -lfreetype -lfontconfig -ljpeg ~/myprefix/lib/libgd.a
...

Il faut également signaler au préprocesseur où aller chercher les fichiers d'entête. On utilise à cette fin la commande ::critcl::cheaders.

::critcl::cheaders {-I${HOME}/myprefix/include}
...

Enfin, on utilise la commande ::critcl::ccode pour "injecter" du code en tête du code C généré qui suivra. C'est typiquement ici que l'on ajoutera les #include, #define, typedef et les variables globales nécessaires dans le reste du code.

::critcl::ccode {
#include <stdlib.h>
#include <stdio.h>
#include <gd.h>
}
...

On peut ensuite attaquer le code de génération de l'image lui même. On va créer une procédure nommée drawTextToFile qui prendra en argument la taille du texte, la police, le texte à écrire et enfin le nom du fichier généré. J'ai choisi arbitrairement le format png pour l'exemple.

::critcl::cproc drawTextToFile {int fontsize char* font char* s char* pngname} int {
	gdImagePtr im;
	FILE *pngout;
	int bgColor;
	int fgColor;
	int brect[8];
	int w, h;
	char *err;
	...

Détaillons un peu.

On déclare ensuite 6 variables intermédiaires pour nos couleurs.

	unsigned long bgR, bgG, bgB, fgR, fgG, fgB;
	bgR = 0xff;
	bgG = 0xff;
	bgB = 0xff;
	fgR = 0;
	fgG = 0;
	fgB = 0;
	...

On signale à gd que l'on va utiliser fontconfig pour rechercher les polices à utiliser. Il faudrait théoriquement ici vérifier le retour de la fonction afin de déterminer si le support fontconfig est disponible ou pas.

	gdFTUseFontConfig(1);
	...

On supprime les retours à la ligne en fin de chaîne.

	while (s[strlen(s)-1]=='\n' || s[strlen(s)-1]=='\r') s[strlen(s)-1]='\0';
	...

Enfin on détermine la taille d'image nécessaire pour notre texte.

	err = gdImageStringFT(NULL,&brect[0],0,font,fontsize,0.,0,0,s);
	if (err) {
		return TCL_ERROR;
	}

Les arguments pris par gdImageStringFT sont les suivants:

Si cette instruction se passe mal, on renvoit TCL_ERROR à l'interpréteur pour faire savoir qu'il y a eu un problème.

On détermine la largeur et la hauteur nécessaire à notre texte.

	w = brect[2]-brect[6];
	h = brect[3]-brect[7];
	...

On crée l'image, on alloue les couleurs nécessaires puis on dessine le texte, "pour de vrai".

	im = gdImageCreate(w,h);
	bgColor = gdImageColorResolve(im, bgR, bgG, bgB);
	fgColor = gdImageColorResolve(im, fgR, fgG, fgB);

	err = gdImageStringFT(im,&brect[0],fgColor,font,fontsize,0.,-brect[6],-brect[7],s);
	if (err) {
		return TCL_ERROR;
	}
	...

On finit.

	pngout = fopen(pngname, "w");
	gdImagePng(im, pngout);
	fclose(pngout);
	gdImageDestroy(im);
	return TCL_OK;
 }
  • On sauve ensuite notre travail dans drawText.tcl et on peut d'ores et déjà le tester.
  • $ tclkit
    % source critcl.kit
    % source drawText.tcl
    % drawTextToFile 48 Mono:Bold "test" mono.png
    0
    % 
    

    Notez que dans tclkit, on peut directement "sourcer" un .kit.

    La valeur 0 correspond à TCL_OK, en cas d'erreur drawTextToFile renverrait 1. On verra plus loin comment apporter plus de précisions à notre utilisateur.

    Votre répertoire de travail contient alors un fichier mono.png du plus bel effet ...

    Mon extension drawText

    Tout cela fonctionne mais n'est pas vraiment pratique, on va donc générer une extension drawText. Tout a été pensé dans ce sens et ce n'est donc qu'une formalité ...

    Il suffit d'ajouter une ligne en tête du fichier drawText.tcl:

    package provide drawText 1.0

    Puis d'invoquer la commande suivante

    $ tclkit critcl.kit -pkg drawText    
    Source: drawText.tcl 
    Library: drawText.dylib
    Package: /Users/zazou/Devel/Tcl-Tk/critcl/lib/drawText
    

    critcl.kit génère pour vous tout ce qu'il faut dans le répertoire lib/drawText, vous pouvez ensuite copier ce répertoire ou Tcl pourra le trouver (ou modifier la variable auto_path) et vous pouvez utiliser ce package comme n'importe quel autre.

    La preuve en image:

    % package require drawText
    1.0
    % drawTextToFile 24 Futura:italic "Bonjour le monde !" futura.png
    0
    

    Tachons de faire propre

    Le code vu précédemment fonctionne mais il reste un problème critique en terme de performances. On passe par un fichier pour charger le png obtenu dans une image Tk.

    Dans le but de supprimer les temps d'accès au disque, on va dans cet exemple retourner directement les données de l'image à l'interpréteur Tcl.

    Dans la foulée, je vous montrerai comment renvoyer des messages à l'utilisateur. En effet TCL_OK et TCL_ERROR sont des valeurs de retour qui ne s'utilisent quand dans les "vraies" commandes Tcl.

    Expliquons donc le code de la nouvelle procédure drawTextToObj.

    ::critcl::cproc drawTextToObj {Tcl_Interp* interp int fontsize char* font char* s} void {
    	...
    

    On trouve en premier paramètre un pointeur vers l'interpréteur Tcl courant. Ce dernier nous est nécessaire pour afficher les messages d'erreur et renvoyer l'objet contenant les données de l'image à qui de droit. Il n'est cependant pas nécessaire lors de l'appel de la commande d'en tenir compte, ce pointeur nous sera automatiquement transmis par Tcl. Le premier argument de la commande sera donc toujours fontsize au regard de l'utilisateur.

    	gdImagePtr im;
    	int size;
    	char* pngPtr;
    	int bgColor;
    	int fgColor;
    	int brect[8];
    	int w, h;
    	char *err;
    	unsigned long bgR, bgG, bgB, fgR, fgG, fgB;
    	Tcl_Obj* objResult;
    	...
    

    On trouve ci-dessus la déclaration d'un Tcl_Obj, qui est un type abstrait (opaque) permettant d'échanger des données de divers types (entiers, flottants, chaînes de caractères, tableaux d'octets, ...) avec l'interpréteur.

    	bgR = 0xff;
    	bgG = 0xff;
    	bgB = 0xff;
    	fgR = 0;
    	fgG = 0;
    	fgB = 0;
    
    	gdFTUseFontConfig(1);
    
    	while (s[strlen(s)-1]=='\n' || s[strlen(s)-1]=='\r') s[strlen(s)-1]='\0';
    	err = gdImageStringFT(NULL,&brect[0],0,font,fontsize,0.,0,0,s);
    	if (err) {
    		Tcl_SetResult (interp, err, TCL_VOLATILE);
    		return;
    	}
    	...
    

    Ici, en cas d'erreur, on récupère le message généré par gd dans la variable err. On envoit le message d'erreur à l'interpréteur pour qu'il soit affiché à l'utilisateur.

    On utilise à cette fin la fonction Tcl_SetResult qui prend en paramètres:

    	w = brect[2]-brect[6];
    	h = brect[3]-brect[7];
    
    	im = gdImageCreate(w,h);
    	bgColor = gdImageColorResolve(im, bgR, bgG, bgB);
    	fgColor = gdImageColorResolve(im, fgR, fgG, fgB);
    
    	err = gdImageStringFT(im,&brect[0],fgColor,font,fontsize,0.,-brect[6],-brect[7],s);
    	if (err) {
    		Tcl_SetResult (interp, err, TCL_VOLATILE);
    		return;
    	}
    	...
    

    On retrouve ci-dessus un message d'erreur de type TCL_VOLATILE.

    	pngPtr = gdImagePngPtr(im, &size);
    	gdImageDestroy(im);
    	...
    

    Arrivé au terme du 'traitement d'image', au lieu de l'envoyer dans un fichier avec gdImagePng comme nous l'avions fait précédemment, on la stocke dans une zone mémoire à l'aide de la fonction gdImagePngPtr qui retourne un pointeur, pngPtr, sur le premier octet de la zone mémoire contenant les données de l'image Png et affecte la taille de cette zone à la variable size.

    	objResult = Tcl_NewByteArrayObj(pngPtr, size);
    	...
    

    Forts des renseignements acquis, on peut maintenant créer un objet de type tableau d'octets suffisamment grand pour contenir les données de l'image. On passe l'adresse des données à y copier en premier paramètre de la fonction Tcl_NewByteArrayObj et la taille des données en second.

    	if (!objResult) {
    		Tcl_SetResult (interp, "Erreur lors de la création de l'objet image.", TCL_STATIC);
    		return;
    	}	
    	...
    

    On vérifie que la création de l'objet s'est bien passée. Dans le cas contraire, on fait parvenir un message à l'interpréteur. Le message étant ici statique, on utilise la fonction Tcl_SetResult avec le type TCL_STATIC évoqué quelques lignes plus haut.

    	Tcl_SetObjResult(interp, objResult);
    	...
    

    On déclare que l'objet objResult sera le résultat de la procédure en appelant Tcl_SetObjResult.

    	gdFree(pngPtr);
    }
    

    Enfin, on libère le buffer utilisé pour stocker l'image ...

    Le code tcl pour utiliser la nouvelle procédure est donné ci dessous. Notez que vous devez disposer du package Img pour le tester. Vous trouverez le lien vers le site en fin d'article.

    % package require Tk
    8.4
    % package require img::png
    1.3
    % package require drawText
    1.0
    % set data [drawTextToObj 24 Futura:italic "Test Obj"]
    ‰PNG 
    ...
    % image create photo -format png -data $data
    image1
    % pack [label .l -image image1]
    

    On voit en retour de la procédure drawTextToObj le contenu de l'image, au format png, affecté à la variable data. On s'en sert ensuite pour créer une image, et voilà !

    Utilisation avancée

    ::critcl::tk

    Cette commande informe critcl que nous aurons également besoin de la partie tk pour nos fonctions, les headers concernant Tk sont alors ajoutés au fichiers générés et on peut alors travailler avec des images, des widgets, etc ...

    ::critcl::csources

    Cette commande permet de spécifier à critcl qu'il devra compiler des sources C supplémentaires pour construire l'extension. Ceci permet par exemple de faire son développement de fonctions de façon classique. Le fichier C étant ensuite ajouté au projet Tcl, une fois mis au point. Comme spécifié plus loin, les modifications de ces sources ne sont pas prises en compte par critcl pour déterminer s'il doit ou non reconstruire la librairie. On doit donc, le cas échéant, le forcer à recompiler.

    A titre d'exemple, on peut reprendre notre fonction somme et l'écrire dans un fichier somme.c.

    int csomme (int a, int b) {
    	return a+b;
    }
    

    somme.tcl contient l'initialisation nécessaire à critcl et spécifie qu'il faudra utiliser somme.c pour construire les fonctions qui suivent.

    package require critcl
    ::critcl::csources somme.c
    ::critcl::cproc somme {int a int b} int {
    	return csomme (a, b);
    }
    

    On utilise ensuite la procédure somme de la même façon.

    % source critcl.kit
    % source somme.tcl
    % somme 4 5
    9
    

    ::critcl::tsources

    Cette commande permet d'ajouter des sources .tcl nécessaires à nos extensions. Les fichiers sont ajoutés dans les extensions, dans un sous répertoire Tcl.

    ::critcl::config keepsrc 1

    Critcl utilise un sous répertoire .critcl de votre répertoire utilisateur. Il y génère des fichiers sources à partir desquels il crée une librairie dynamique, plus tard chargée par Tcl.

    Cette commande spécifie à critcl de conserver les sources intermédiaires. Ce qui peut être utile pour du débugage ou si vous ne voulez utiliser critcl que pour vous générer un squelette d'extension Tcl.

    ::critcl::config force 1

    On peut grâce à cette commande forcer critcl à recompiler la librarie dynamique à chaque chargement. Cela permet de passer outre le fait que critcl ne détecte pas les changements survenus dans les fichiers liés par la commande ::critcl::csources, par exemple (on obtient le même comportement en utilisant le switch -force lors de l'utilisation de critcl.kit).

    ::critcl::ccommand

    critcl::ccommand procname {dummy interp objc objv} {
    ...
    }
    

    C'est une méthode plus puissante pour créer de nouvelles commandes. On doit spécifier le nom de la commande à créer et le noms des variables transmises.

    dummy ne sert pas souvent, c'est pourquoi j'ai choisi de ne pas vous en parler. Reporter vous au manuel pour en savoir plus.

    On trouve ensuite l'interpréteur Tcl, que nous devrons réutiliser dans toutes les fonctions le nécessitant.

    objc et objv sont les équivalents de argc et argv en C. objc contient le nombre d'arguments contenus dans objv, le tableau d'arguments. Tcl met à notre disposition des fonctions pour extraire la valeur typée de objv (Tcl_GetString, Tcl_GetBoolean, Tcl_GetInteger, ...). Vous les trouverez également ans la Tcl Library en ligne.

    Réécrire vos cproc en ccommand n'apporte pas grand chose si vous n'avez pas besoin d'utiliser un nombre variable d'arguments mais dans le cas contraire, c'est un passage obligé.

    Notre cproc somme réécrite en ccommand se présente ainsi:

    package require critcl
    ::critcl::csources somme.c
    ::critcl::ccommand somme {dummy interp objc objv} {
    	int a, b, s;
    	Tcl_Obj* o;
    	
    	if (objc <= 2) {
    		Tcl_WrongNumArgs(interp, 1, objv, "a b");
    		return TCL_ERROR;
    	}
    	Tcl_GetIntFromObj(interp, objv[1], &a);
    	Tcl_GetIntFromObj(interp, objv[2], &b);
    	
    	o = Tcl_NewIntObj (csomme (a, b));
    	Tcl_SetObjResult (interp, o);
    	
    	return TCL_OK;
    }
    

    C'est plus long, certes mais on pourrait ainsi très facilement ajouter d'autres nombres à notre somme.

    Pour ce qui est des nouveautés :

    Un cas concret

    Si cette introduction à critcl vous a donné envie d'en savoir plus, vous pouvez consulter les sources d'une extension basée sur critcl. La société La Rochelle Innovation, qui héberge le wiki Tcl francophone a récemment publié une extension permettant d'appliquer divers traitements à une image nommée LRIPhoto. L'extension est disponible sous forme d'un kit. La commande tclkit sdx.kit unwrap LRIPhoto.kit décompressera le kit et vous pourrez consulter les sources dans le répertoire ./LRIPhoto.vfs/lib/LRIPhoto/src.

    Cet article a été principalement écrit sous Debian. Les tests ont été réalisés sous debian, ubuntu linux, Mac OSX 10.3 et MSYS. Ce qui montre bien la facilité de portage des extensions Tcl écrites à l'aide de critcl.

    Si vous souhaitez voir d'autres articles traitant de Tcl dans Linuxmag, n'hésitez pas à entrer en contact avec moi.

    Liens & Références :

    a+

    Auteur : Xavier GARREAU
    Modifié le 22.12.2005

    Rechercher :

    Google
     
    Web www.xgarreau.org