Embeddable Common Lispの環境構築
Common Lispを最近触りはじめて、Roswellなどの便利なツールを使ったほうが良いのかもしれないと思いつつ、周辺ライブラリやシステムがどのように設定されてどう動いているのかを確認したかったので、ECL環境を構築した手順をまとめておく。
環境
- Debain 10.2
はじめに
今回構築した環境をワンコマンドで作るスクリプトを作成した。プロジェクト雛形を作成したり、REPLやEmacs&SLIMEの起動、テストやビルド、パッケージインストールなどをシェルから実行するためのフロントエンドを含む。が、他の人の環境で動くかは不明。。。
ECLとEmacsのインストール
ECLとEmacsはDebianパッケージとして提供されているものを利用する。
$ sudo apt install ecl emacs
ディレクトリ構成
~/.localもしくは好きなディレクトリで、以下のような形でディレクトリ構造を作成する。lib以下には、ASDFや個別にインストールするCommon Lispのパッケージを設置し、share/deepspace以下にはquicklisp、slime、その他の設定ファイルやスクリプトをを設置する。
.
├── lib
| ├── asdf.lisp
│ └── ecl-readline-0.4.1
├── share
│ └── deepspace
│ ├── quicklisp
│ ├── scripts
│ └── slime-2.24
└── src
ASDFのインストール
DebianのECLパッケージをインストールするとASDFもついてくるが、今回は個別にASDFを入れてそれを使うように設定する。今後、~/deepspace
以下に色々と展開していく。
まず、ECLのためのいくつかの修正を加えたASDFのフォークが存在するので、それをクローンしてきてビルドする。
$ mkdir -p ~/deepspace/src
$ cd ~/deepspace/src
$ git clone git@common-lisp.net:ecl/asdf.git
$ cd asdf
$ make
makeの後、asdf/build以下にasdf.lispというファイルができるので、これを~/deepspace/lib/asdf.lisp
に設置する。
$ mkdir ~/deepspace/lib
$ cp build/asdf.lisp ~/deepspace/lib/
SLIMEのインストール
Debianパッケージに含まれているものが2.23のため、ソースからインストールする。
$ cd ~/.local/src
$ wget -O slime-2.24.tar.gz https://github.com/slime/slime/archive/v2.24.tar.gz
$ tar xf slime-2.24.tar.gz
$ mkdir -p ~/deepspace/share/deepspace
$ mv slime-2.24 ~/deepspace/share/deepspace/
$ rm slime-2.24.tar.gz
Quicklispのインストール
Quicklispのインストーラーも~/deepspace/src
以下に保存し、インストール先は~/deepspace/share/deepspace/quicklisp
にする。
$ cd ~/deepspace/src
$ wget https://beta.quicklisp.org/quicklisp.lisp
$ ecl --norc --eval "(load \"quicklisp.lisp\")" --eval "(quicklisp-quickstart:install :path \"~/deepspace/share/deepspace/quicklisp\")" --eval "(quit)"
ECL Readlineの導入
ECL標準のREPLは使いづらいのと、わざわざEmacsとSLIMEを立ち上げたくないときのことを考えて、ECL Readlineを使う。
$ cd ~/deepspace/src
$ wget http://www.common-lisp.net/project/ecl-readline/releases/ecl-readline-0.4.1.tar.gz
$ tar xzvf ecl-readline-0.4.1.tar.gz
$ mv ecl-readline-0.4.1 ~/deepspace/lib/
libreadlineが必要になるので、次のようにしてパッケージもインストールしておく。
$ sudo apt install libreadline-dev
ECLの設定
ECLの設定の前に、環境変数をひとつ新しく設定する。DEEPSPACE_HOMEという環境変数に~/deepspace
を登録し、ECLやEmacsの設定からそれを参照して必要なライブラリや設定ファイルを読み込むようにするためだ。
export DEEPSPACE_HOME=~/deepspace
ECLは通常$HOME/.eclrc
を設定ファイルとして読み込むようになっている。が、ホームディレクトリを汚したくないので、~/deepspace/share/deepspace/.eclrc
に新しく作成する。
(defparameter *deepspace-home* (si:getenv "DEEPSPACE_HOME"))
(load (concatenate 'string *deepspace-home* "/lib/asdf.lisp"))
(require 'asdf)
(setf asdf:*asdf-verbose* nil)
(setf *load-verbose* nil)
(asdf:initialize-source-registry
`(:source-registry
(:tree ,(concatenate 'string *deepspace-home* "/share/deepspace/quicklisp/dists/quicklisp/software"))
(:tree ,(concatenate 'string *deepspace-home* "/lib/"))
(:tree ,(namestring *default-pathname-defaults*))
:inherit-configuration))
(asdf:initialize-output-translations
'(:output-translations
:enable-user-cache
:ignore-inherited-configuration))
(asdf:load-system :ecl-readline)
(ecl-readline::enable)
lib/asdf.lispのロード、ASDFが読み込むパッケージの場所の設定、ECL Readlineの設定を行っている。
.eclrcの以下の部分はASDFのパッケージロード先を登録するものだ。*deepspace-home*
という変数を展開するためにクォートした式の中でカンマを入れている(ここは展開できずにかなり悩んだ)。Lispのマクロを作る際によく使われるみたいだが、クォートした式の中で、カンマ以降の式は通常どおり評価される(と認識している)。
(asdf:initialize-source-registry
`(:source-registry
(:tree ,(concatenate 'string *deepspace-home* "/share/deepspace/quicklisp/dists/quicklisp/software"))
(:tree ,(concatenate 'string *deepspace-home* "/lib/"))
(:tree ,(namestring *default-pathname-defaults*))
:inherit-configuration))
ASDFは、asdf:*source-registry-parameter*
という変数でパッケージを探しにいく場所を決めている。上記は、Quicklispによってインストールされたパッケージ、~/deepspace/lib
以下にあるパッケージ、今いる場所以下のパッケージを読みにいくよう設定している。
ASDFのマニュアルによれば、パッケージを探す際は以下のディレクトリを標準で探しにいく。
~/common-lisp/
~/.local/share/common-lisp/source/
また、探しにいくディレクトリは、source-registry.conf.d以下のconfファイルか、source-registry.confに記述することでも対応できる。
~/.config/common-lisp/source-registry.conf.d/*.conf
~/.config/common-lisp/source-registry.conf
のだけど、あまりポータブルな感じがしないのでこれで設定することはしない。
EmacsとSLIMEの設定
EmacsもSLIMEも入れているので、Emacs側の設定を行う。Emacsは通常、~/.emacs.d/init.el
を設定ファイルとして読み込むが、これも既存の設定を汚したくないので~/deepspace/share/deepspace/.emacs.d/init.el
という新しいファイルを作成してそこに記述する。
(setq slime-net-coding-system 'utf-8-unix)
(setq inferior-lisp-program
(concat "ecl --norc "
(concat " --load " (expand-file-name (concat (getenv "DEEPSPACE_HOME") "/share/deepspace/.eclrc")))))
(add-to-list 'load-path (expand-file-name
(concat (getenv "DEEPSPACE_HOME")
"/share/deepspace/slime-2.24")))
(require 'slime)
(slime-setup '(slime-repl slime-fancy slime-banner))
(slime)
ECL REPLとEmacs & SLIME REPLの動作確認
ECLは--norc
オプションで標準の設定ファイルを読み込まなくなる。--loadを使って先ほど使った.eclrcを読み込み起動する。
$ ecl --norc --load "~/deepspace/share/deepspace/.eclrc"
~
CL-USER[1]>
CL-USER[1]>
というプロンプトが表示されれば問題ない。ECL Readlineを使っていない場合は、>
というプロンプトが表示される。(quit)
で終了する。
Emacsは-q
オプションで標準の設定ファイルを読み込まなくなる。先ほど作成したinit.elは自動でSLIMEを起動するため、以下のコマンドでEmacsとSLIME REPLの環境が起動する。
$ emacs -q --load "~/deepspace/share/deepspace/.emacs.d/init.el"
; SLIME 2.24
CL-USER>
というプロンプトが表示されれば成功だ。こちらも、(quit)
で終了する。