Installer un environnement Common Lisp (SBCL) sous Emacs

[size=200]Introduction[/size]

Pas vraiment la peine de présenter Common Lisp : ceux qui connaissent l’apprécient pour ce qu’il est, et les autres ne savent pas ce qu’ils ratent… :stuck_out_tongue:
Pour les débutants qui voudraient apprendre les bases de Lisp, qui reste l’un des très rares langages de programmation programmables, je vous conseille fortement de lire Practical Common Lisp de Peter Seibel. Pour en apprendre plus, lisez également On Lisp de Paul Graham. Une fois ces deux ouvrages digérés vous aurez déjà de bonnes bases.

Sur Linux, une des implémentations les plus abouties de Common Lisp (et qui a l’avantage d’évoluer très rapidement) est Steel Bank Common Lisp (SBCL), dérivée de CMU-CL.
Emacs couplé à SLIME et SBCL est l’environnement idéal que ce soit pour du prototypage rapide ou du développement plus poussé. C’est ce que je me propose d’installer dans ce T&A, avec bien entendu l’HyperSpec (la documentation de référence de Common Lisp) disponible en local.

Inutile de compter sur les dépôts Debian pour maintenir votre SBCL à jour : les paquets sont mis à jour tous les 36 du mois, et une fois sur deux toute l’installation part en sucette, obligeant à bricoler les fichiers de la distribution. Cet état de fait tient à la volonté (qui part d’un bon sentiment) de fournir tout ce qu’il faut pour faire cohabiter proprement différentes implémentations de Common Lisp sur une même Debian via une architecture ASDF commune. Malheureusement le résultat est inutilisable à l’heure actuelle, c’est bien dommage.

La meilleure solution pour avoir un environnement à la fois robuste et à jour reste donc de tout installer à la main. Ça veut aussi dire être capable de recompiler SBCL.
Ne nous leurrons pas, très peu de personnes ont besoin d’avoir plusieurs implémentations différentes sur la même machine, et ces gens là n’ont besoin ni de ce T&A ni des dépôts Debian… Pour un usage commun (lire : tant qu’on ne développe pas de librairies cross-implémentations), il est très largement suffisant de ne disposer que de SBCL.
Compiler soi-même permet également d’accéder aux sources de SBCL à partir d’Emacs, ce qui peut se révéler bien pratique par moments.

En résumé : ce T&A vise les développeurs Lisp (ou wannabe…) qui souhaitent maintenir à jour un environnement SBCL/SLIME/Emacs complet sans se prendre la tête, et qui comme moi en ont marre de devoir recoller les morceaux à chaque fois.

IMPORTANT : respectez bien les droits des commandes utilisateur ($) et root (#) ça évitera les bêtises.

[size=200]Sommaire[/size]

[ul][li] Amorçage de SBCL à partir d’une distribution binaire[/li]
[li] Compilation de SBCL[/li]
[li] Installation de SLIME[/li]
[li] Installation de LispWorks HyperSpec[/li]
[li] Installation de Emacs[/li]
[li] Configuration de SBCL et SLIME[/li]
[li] Configuration basique de Emacs[/li]
[li] Maintenir l’ensemble à jour[/li][/ul]

[size=200]Organisation[/size]

Je vais installer tout ça dans /opt/lisp/ mais vous pouvez bien sûr le mettre où vous voulez.
Afin de rendre les mises à jour totalement transparentes, on créera un lien /opt/lisp/sbcl/ qui pointera vers la version courante de SBCL.

SLIME et l’HyperSpec atterriront aussi dans /opt/lisp/.
SBCL sera au final installé manuellement dans /usr/local/ via des liens symboliques. Changez ça à vos risques et périls (n’oubliez surtout pas de définir SBCL_HOME dans ce cas).

Note : l’ensemble étant un peu long, j’ai séparé les sections en plusieurs messages à la fois pour des raisons de clarté, et pour pouvoir faire un sommaire avec des liens vers les différentes parties.

[size=200]Amorçage de SBCL à partir d’une distribution binaire[/size]

Note : j’ai sciemment séparé cette étape afin de fournir une procédure de compilation qui fonctionne aussi bien pour la compilation d’amorçage que pour les mises à jour à l’avenir.

Pour mémoire, SBCL nécessite une implémentation Common Lisp fonctionnelle pour pouvoir être compilé. Le plus simple est de télécharger une distribution binaire de SBCL qui corresponde à votre plate-forme : sbcl.org/platform-table.html
En ce qui me concerne j’ai utilisé la 1.0.37 pour AMD64 (sbcl-1.0.37-x86-64-linux-binary.tar.bz2).

Une fois téléchargée, nous allons installer cette distribution binaire « comme si » il s’agissait d’une distribution source complète.
SBCL fournit une procédure pour installer ses fichiers dans /usr/local/. Je vous déconseille de l’utiliser telle quelle, ça demanderait du boulot inutile lors de chaque mise à jour (et notamment lors de la compilation qui va suivre). C’est pourquoi nous allons plutôt créer des liens symboliques à la main, l’avantage étant bien sûr qu’à l’avenir en changeant simplement le lien symbolique /opt/lisp/sbcl/ on mettra à jour l’ensemble de la bête.
Si vous souhaitez l’installer ailleurs que dans /usr/local/ libre à vous, c’est pas moi qui vais vous en empêcher (mais je vais pas vous aider non plus)…

[code]$ cd /répertoire/de/téléchargement/
$ tar xjf sbcl-1.0.37-x86-64-linux-binary.tar.bz2

mkdir /opt/lisp

mv sbcl-1.0.37-x86-64-linux/ /opt/lisp/sbcl-bootstrap

$ cd /opt/lisp/sbcl-bootstrap/ && INSTALL_ROOT=/opt/lisp/sbcl-bootstrap/distrib/ sh ./install.sh

ln -s /opt/lisp/sbcl-bootstrap/ /opt/lisp/sbcl

ln -s /opt/lisp/sbcl/distrib/bin/sbcl /usr/local/bin/

ln -s /opt/lisp/sbcl/distrib/lib/sbcl/ /usr/local/lib/

mkdir -p /usr/local/share/doc/

ln -s /opt/lisp/sbcl/distrib/share/doc/sbcl/ /usr/local/share/doc/

mkdir -p /usr/local/share/man/man1/

ln -s /opt/lisp/sbcl/distrib/share/man/man1/sbcl.1 /usr/local/share/man/man1/[/code]

[size=200]Compilation de SBCL[/size]

Télécharger les sources les plus récentes (juste au dessus du tableau des plate-formes) : sbcl.org/platform-table.html

En ce qui me concerne j’ai utilisé la 1.0.37 (sbcl-1.0.37-source.tar.bz2). Dans les commandes qui vont suivre, n’oubliez pas de remplacer cette version par celle que vous avez réellement téléchargée.

Note : il est tout à fait normal d’avoir des sources plus récentes que la distribution binaire, SBCL évolue tellement vite (généralement une version par mois) qu’ils ne refont pas forcément des builds pour toutes les plate-formes à chaque version. En fait, il est même assez rare d’avoir des sources qui correspondent à la distribution binaire.

Pour Linux i686 et AMD64, la configuration par défaut fait très bien l’affaire (ce qui n’était pas le cas il y a encore peu de temps). Si vous tenez à configurer des aspects spécifiques de SBCL, il vous faudra éditer le fichier customize-target-features.lisp (voir la documentation INSTALL pour plus de détails).

IMPORTANT : la compilation « en place » (ie. à l’emplacement même où se trouveront les fichiers une fois tout fini) est sinon nécessaire mais tout au moins d’une grande aide pour pouvoir accéder aux sources de SBCL à partir d’Emacs. Par le passé j’ai eu des cas où Emacs ne retrouvait pas ses petits après avoir déplacé le dossier une fois la compilation finie, alors qu’en compilant dans l’emplacement définitif tout va comme sur des roulettes.

[code]$ cd /répertoire/de/téléchargement/
$ tar xjf sbcl-1.0.37-source.tar.bz2

mv sbcl-1.0.37/ /opt/lisp/

$ cd /opt/lisp/sbcl-1.0.37/ && sh make.sh[/code]Allez prendre un café (ou une camomille suivant l’heure) et attendez que ça se passe.

Une fois la compilation finie , on va maintenant vérifier que tout fonctionne correctement.
ATTENTION : ces tests peuvent être extrêmement lourds, il vaut donc mieux prendre les précautions suivantes :

  • fermer toutes vos applications
  • # sync pour garantir que vous ne perdrez aucune donnée si ça se passe mal
  • # swapoff -a pour éviter que les tests ne consomment tout votre swap
  • IMPORTANT : si votre CPU permet d’ajuster sa fréquence dynamiquement en fonction de la charge, poussez-le au maximum ! C’est indispensable pour les tests qui vérifient la synchronisation multithread (notamment le test UNSYNCHRONIZED-HASH-TABLE qui tente de provoquer une race-condition afin de voir si SBCL la détecte comme prévu ; à fréquence minimum il va juste bouffer toute votre RAM sans arriver à ses fins)
    Vous êtes prévenus.

$ cd /opt/lisp/sbcl-1.0.37/tests/ && sh ./run-tests.shDeuxième café (ou une verveine pour changer) mais surveillez-le du coin de l’œil, s’il passe plus de 5-10mn (avec une machine “moderne”) sur un même test sans avancement visible c’est qu’il a probablement planté.

Si tout fonctionne correctement, vous devriez obtenir quelque chose comme ça :

Status: Expected failure: callback.impure.lisp / UNDERFLOW-DETECTION Expected failure: debug.impure.lisp / (UNDEFINED-FUNCTION BUG-353) Expected failure: packages.impure.lisp / USE-PACKAGE-CONFLICT-SET Expected failure: packages.impure.lisp / IMPORT-SINGLE-CONFLICT //apparent success (reached end of run-tests.sh normally)Note : tout « Unexpected failure / success » implique un bug quelconque, dans ce cas faites une recherche pour savoir si c’est un bug connu pour la version de SBCL concernée (c’est le cas la plupart du temps), ou bien s’il est spécifique à votre cas (très rare).

Vous pouvez maintenant réactiver votre swap : # swapon -a :stuck_out_tongue:

Si vous voulez également compiler les manuels, il vous faudra LaTeX :

# aptitude --without-recommends install texlive-latex-base $ cd /opt/lisp/sbcl-1.0.37/doc/manual && make

Tout est prêt, il n’y a plus qu’à…

Isoler les fichiers distribuables de SBCL dans leur propre sous-répertoire (même structure que dans la section précédente, pour pouvoir interchanger les versions de manière transparente) :

Nettoyer les fichiers temporaires (en prenant soin de conserver les fichiers récemment compilés) :

$ mv /opt/lisp/sbcl-1.0.37/distrib/ /tmp/sauvegarde-sbcl-distrib $ cd /opt/lisp/sbcl-1.0.37/ && sh ./clean.sh $ mv /tmp/sauvegarde-sbcl-distrib/ /opt/lisp/sbcl-1.0.37/distrib
Verrouiller l’ensemble du répertoire SBCL pour qu’il ne soit plus modifiable par des utilisateurs :

On peut maintenant « détacher » l’ancienne version de SBCL et la remplacer par la nouvelle :

[code]# rm /opt/lisp/sbcl/

ln -s /opt/lisp/sbcl-1.0.37/ /opt/lisp/sbcl[/code]

N’oubliez pas, une fois la nouvelle version validée par vos soins (donc, pas tout de suite), de supprimer l’ancienne. En l’occurrence :

[size=200]Installation de SLIME[/size]

On va simplement télécharger la toute dernière révision du CVS.
Pas d’inquiétude à avoir, SLIME est un projet très stable, le CVS convient donc parfaitement.

[code]$ cd /n’importe/où/
$ cvs -d :pserver:anonymous:anonymous@common-lisp.net:/project/slime/cvsroot co slime

mv slime/ /opt/lisp/ && chown -R root:root /opt/lisp/slime/[/code]

[size=200]Installation de LispWorks HyperSpec[/size]

Télécharger le tarball. Si par malheur ce lien direct venait à expirer, rendez-vous sur la page web de LispWorks et fouinez un peu (« This HTML document may, subject to certain conditions, be downloaded for use at other locations. ») pour retrouver le bon fichier.

[code]$ cd /répertoire/de/téléchargement/
$ tar xzf HyperSpec-7-0.tar.gz

mv HyperSpec /opt/lisp/ && chown -R root:root /opt/lisp/HyperSpec/[/code]

En bonus, une feuille de styles un peu plus lisible (à mon goût) que celle par défaut. Faites quand même une sauvegarde de l’ancienne au cas où la mienne ne vous convienne pas (elle est un tantinet colorée, tout le monde ne supporte pas forcément même si personnellement je trouve ça énormément plus efficace pour bosser).
Le fichier à éditer est /opt/lisp/HyperSpec/Data/clhs.css, remplacer son contenu par :

html,body { background-color: black; color: white; } hr { color: #218AE7; } img,a img { border-color: black; } /* liens par défaut */ a,a:visited { color: #8080FF; text-decoration: none; } a:hover,a:active,a:focus { color: #A0A0FF; text-decoration: underline; } /* code & code inline */ pre,tt { color: #FFFF80; } /* selon votre navigateur ça peut être nécessaire (ça l'est sur mon iceweasel) */ pre,tt { font-size: 125%; } /* points remarquables */ i { font-weight: normal; font-style: normal; color: #FF8080; } /* glossaire */ a i { font-weight: normal; font-style: normal; color: #A0FFA0; }

[size=200]Installation de Emacs[/size]

On va utiliser la version Debian, qui est très bien maintenue. Tout de suite ça simplifie les choses !

[size=200]Configuration de SBCL et SLIME[/size]

Note : si vous avez des tuyaux intéressants pour la configuration de SBCL, n’hésitez pas à les partager ça peut servir à d’autres !

Un des points les plus importants importants est la configuration de ASDF (le moteur de chargement de librairies) et ASDF-INSTALL (le moteur de téléchargement / installation desdites librairies).

Personnellement j’utilise uniquement la structure de répertoires suivante :

~/dev/lisp/ => tout ce qui a trait au code Lisp ~/dev/lisp/asdf/systems/ => mes fichiers SYSTEM personnels ~/dev/lisp/asdf/install/ => tout ce qui concerne ASDF-INSTALL ~/dev/lisp/asdf/install/site/ => code source des librairies téléchargées ~/dev/lisp/asdf/install/systems/ => fichiers SYSTEM pour que ASDF puisse y accéderSi ça ne vous convient pas, vous êtes assez grands pour adapter en fonction de vos besoins. Dans tous les cas, il faudra veiller à créer l’arborescence que vous aurez choisie.

En conséquence de quoi, voici mon fichier ~/.sbclrc :

[code];;
;; Recompiler automatiquement les FASL (fichiers compilés) en cas de souci (une seule fois)
;;
(require :asdf)
(defmethod asdf:perform :around ((o asdf:load-op)
(c asdf:cl-source-file))
(handler-case (call-next-method o c)
(sb-ext:invalid-fasl ()
(asdf:perform (make-instance 'asdf:compile-op) c)
(call-next-method))))

;;
;; ASDF : trop verbeux à mon goût, même comme ça ça reste vraiment limite…
;;
(setf asdf::load-verbose nil)
(setf asdf::load-print nil)
(setf asdf::compile-verbose nil)
(setf asdf::compile-print nil)

;;
;; ASDF : répertoires des liens SYSTEM
;; ~/dev/lisp/asdf/systems/
;; librairies personnelles
;; ~/dev/lisp/asdf/install/systems/
;; librairies ASDF-INSTALL
;;
(setf asdf:central-registry nil)
(push (merge-pathnames “dev/lisp/asdf/systems/” (user-homedir-pathname)) asdf:central-registry)
(push (merge-pathnames “dev/lisp/asdf/install/systems/” (user-homedir-pathname)) asdf:central-registry)

;;
;; ASDF-INSTALL : répertoires des librairies et des liens SYSTEM
;;
(require :asdf-install)
(setf asdf-install:locations nil)
(push `(,(merge-pathnames “dev/lisp/asdf/install/site/” (user-homedir-pathname))
,(merge-pathnames “dev/lisp/asdf/install/systems/” (user-homedir-pathname))
“User installation”)
asdf-install:locations)[/code]
Configuration de SLIME :

$ cat > ~/.swank.lisp << HEREDOC (setq swank:*globally-redirect-io* t) (setq swank:*global-debugger* t) HEREDOC

[size=200]Configuration basique de Emacs[/size]

Le but ici n’est pas de discuter des infinies possibilités de configuration d’Emacs, mais simplement de faire fonctionner SBCL et SLIME dans cet environnement.
Les T&A Emacs directement en rapport avec SBCL, Common Lisp ou SLIME pourront éventuellement être ajoutés à ce fil, mais pour tout ce qui concerne uniquement Emacs mieux vaut démarrer un autre sujet sinon on va jamais s’en sortir.

À rajouter à votre ~/.emacs ou dans tout autre fichier qui sera exécuté au démarrage de Emacs :

[code](setq auto-mode-alist
(append '((".lisp$" . lisp-mode)
(".lsp$" . lisp-mode)
(".cl$" . lisp-mode)
(".asd$" . lisp-mode)
(".system$" . lisp-mode))
auto-mode-alist))

(defun sbcl ()
(interactive)
;; Paths to SBCL, SLIME and HyperSpec
(setq inferior-lisp-program “/usr/local/bin/sbcl”)
(setq slime-net-coding-system 'utf-8-unix)
(setq common-lisp-hyperspec-root “/opt/lisp/HyperSpec/”)
(add-to-list 'load-path “/opt/lisp/slime/”)
;; Setup SLIME
(require 'slime)
(add-hook 'lisp-mode-hook (lambda () (slime-mode t)))
(slime-setup '(slime-repl slime-c-p-c))
(slime)
;; Prevent annoying “Active processes exist” query when you quit Emacs.
(defadvice save-buffers-kill-emacs (around no-query-kill-emacs activate)
(flet ((process-list ())) ad-do-it))

;;
;; Ci dessous, quelques mode-maps pour rendre la vie un peu plus facile.
;;

;; Homogénéiser Lisp-Mode / REPL
(define-key slime-mode-map (kbd “TAB”) 'slime-indent-and-complete-symbol)
(define-key slime-mode-map [M-return] 'slime-close-all-parens-in-sexp)
;; Aide
(define-key slime-mode-map [f1] 'slime-hyperspec-lookup)
(define-key slime-repl-mode-map [f1] 'slime-hyperspec-lookup)
(define-key slime-mode-map [S-f1] 'slime-describe-symbol)
(define-key slime-repl-mode-map [S-f1] 'slime-describe-symbol)
;; Contrôle du REPL
(define-key slime-mode-map [f6] 'slime-restart-inferior-lisp)
(define-key slime-repl-mode-map [f6] 'slime-restart-inferior-lisp)
(define-key slime-repl-mode-map [f7] 'slime-repl-clear-buffer)
;; Contrôle du buffer
(define-key slime-mode-map [f8] 'slime-eval-buffer)
(define-key slime-mode-map [f9] 'slime-eval-defun)
)[/code]

Démarrer Emacs, M-x sbcl lance le REPL au bout de quelques instants (la première compilation prend toujours quelques secondes).

[size=200]Maintenir l’ensemble à jour[/size]

HyperSpec : la version actuelle (7.0) date de 2005 et est tout à fait complète par rapport au standard. Très peu de chances donc que ça change.

Emacs : géré par les dépôts Debian.

SLIME : vous pouvez faire un # cvs update -P -d dans /opt/lisp/slime/ ou bien simplement supprimer ce répertoire et recommencer l’installation de SLIME. Personnellement j’utilise la deuxième méthode, à vous de voir. Pensez à nettoyer (lire : supprimer) les FASL en cache dans ~/.slime/.

SBCL : il suffit de suivre à nouveau l’étape d’installation de SBCL à la lettre. Pensez à nettoyer (lire : supprimer) les FASL en cache dans ~/.slime/ et éventuellement le répertoire de la version précédente de SBCL une fois la nouvelle version validée. Pour ce qui est des FASL de vos projets ou de ASDF-INSTALL, le (defmethod asdf:perform :around …) dans ~/.sbclrc se charge de les recompiler après une montée de version.