Embeddable Common Lispの環境構築

# Embeddable Common Lispの環境構築

Common Lispを最近触りはじめて、Roswellなどの便利なツールを使ったほうが良いのかもしれないと思いつつ、周辺ライブラリやシステムがどのように設定されてどう動いているのかを確認したかったので、ECL環境を構築した手順をまとめておく。

## 環境

- Debain 10.2

## はじめに
今回構築した環境をワンコマンドで作るスクリプトを作成した。プロジェクト雛形を作成したり、REPLやEmacs＆SLIMEの起動、テストやビルド、パッケージインストールなどをシェルから実行するためのフロントエンドを含む。が、他の人の環境で動くかは不明。。。

https://gitlab.com/hu.moonstone/deepspace

## ECLとEmacsのインストール

ECLとEmacsはDebianパッケージとして提供されているものを利用する。

```bash
$ 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のフォーク](https://gitlab.common-lisp.net/ecl/asdf)が存在するので、それをクローンしてきてビルドする。

```bash
$ 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`に設置する。

```bash
$ mkdir ~/deepspace/lib
$ cp build/asdf.lisp ~/deepspace/lib/
```

## SLIMEのインストール

Debianパッケージに含まれているものが2.23のため、ソースからインストールする。

```bash
$ 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`にする。

```bash
$ 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](https://common-lisp.net/project/ecl-readline/)を使う。

```bash
$ 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が必要になるので、次のようにしてパッケージもインストールしておく。

```bash
$ sudo apt install libreadline-dev
```

## ECLの設定

ECLの設定の前に、環境変数をひとつ新しく設定する。DEEPSPACE_HOMEという環境変数に`~/deepspace`を登録し、ECLやEmacsの設定からそれを参照して必要なライブラリや設定ファイルを読み込むようにするためだ。

```bash:.bashrc
export DEEPSPACE_HOME=~/deepspace
```

ECLは通常`$HOME/.eclrc`を設定ファイルとして読み込むようになっている。が、ホームディレクトリを汚したくないので、`~/deepspace/share/deepspace/.eclrc`に新しく作成する。

```common-lisp:.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のマクロを作る際によく使われるみたいだが、クォートした式の中で、カンマ以降の式は通常どおり評価される（と認識している）。

```common-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のマニュアル](https://privet-kitty.github.io/misc/asdf-ja/index.html)によれば、パッケージを探す際は以下のディレクトリを標準で探しにいく。

- `~/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`という新しいファイルを作成してそこに記述する。

```lisp: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)`で終了する。