秘伝のタレとなったEmacsの設定をgitで管理するなどして, 複数の環境で同じ設定を使うようにするのはかなり一般的になってきました. ただ, 使っている非標準パッケージも含めてきちんと管理しようと思うとけっこう大変です. とくに, 以下のような点はぜひとも実現したいところですが, (これまでは)なかなか難しい部分もありました.
- 使っているパッケージのインストールを自動化したい
- いろいろな配布元(GitHub, Emacs Wiki, 個人Webサイト, etc.)からインストールしたい
- きちんと動くことがわかっているパッケージバージョンに固定したい
- 新しいパッケージを簡単に試したい
- パッケージと設定の対応をわかりやすくしたい
この目的のために, 最近はCaskを使うのが流行っているようですが, 上に挙げたポイントをすべて解決しているわけではありません. 筆者のまわりでも, Caskを使ってみたが思ったことができなくて困った, という話を複数聞きます. 元々パッケージの開発時に依存パッケージの管理をするために作られたもののようなので, 仕方ない面もあるかもしれません.
El-Getは, 自分のEmacsにインストールされているパッケージを管理する目的で作られただけあってより条件を満たしているのですが, 配布元のレシピをいちいち書かないといけない(と誤解されている), 書き方があまり簡単には見えない, などの理由で敬遠されている印象です. 公式レシピに登録されている配布元も, 基本的にはバージョン管理された最新版(≒開発版)を指定するポリシーで, 安定版のみを使いたい場合にはオーバースペックと思われているのかもしれません.
最近になって, (筆者によって)El-Getでも簡単な記法がサポートされて, レシピをいちいち書くことから完全に解放され, 安定版を使う指定も非常に簡単にできるようになりました. さらに, (筆者による)関連ツールを使えば, バージョン固定やコマンドラインでの操作も可能なばかりか, Caskファイルをそのまま読み込むことまでできます. 本稿では, CaskからEl-Getに乗り換える場合の方法も含めて, El-Getによるパッケージ管理のいまどきのやり方を解説します.
El-GetとCaskの比較
El-GetやCaskでEmacsのパッケージを管理する場合に関して, サポートされる機能を整理しておきます. 比較のためにEmacsの標準パッケージ管理システム(package.el
)についても載せておきます.
Emacs標準 | El-Get | Cask | |
---|---|---|---|
VCSから | × | ◎ | ○ |
特定サイトから | × | ◎ | × |
バージョン固定 | × | ○ | △ |
DSL | × | ○ | ○ |
コマンドライン | △ | ○ | ○ |
M-x 実行 |
○ | ◎ | △ |
Emacs以外に依存しない | ◎ | ○ | × |
設定管理 | × | ○ | × |
パッケージ開発者向け機能 | △ | △ | ○ |
El-GetはまずサポートしているVCSの種類が豊富ですが, 実際にはCaskがサポートしている程度で十分でしょう. El-Getのよいところとしては, GitHubやGist, Emacsmirror, Emacs Wikiからのインストールの場合にそれ用の簡単な指定で済む点が挙げられます.
package.el
ではバージョンを固定したインストールができないため, El-GetやCaskでも配布元がELPA系*1の場合はバージョンを固定できません. VCSからのインストールの場合は, El-Get, Caskともにパッケージごとにひとつひとつリビジョンを指定することでチェックアウトするバージョンを固定できますが, これは非常に面倒です. さらに悪いことにCaskでは標準で選択される配布元がELPA系で, VCSからのインストールにはパッケージのURLを指定する必要があります. El-Getは, 公式レシピの配布元もほとんどはVCSになっているため, この点はあまり問題になりません. さらに, el-get-lockというパッケージを使うことで, RubyのGemfile.lock
やPerlのcpanfile.snapshot
のような感覚でバージョンを固定することができます. (あとで詳しく解説します.)
Caskでは, DSLやコマンドラインでインストールするパッケージを指定できます. El-Getではそれぞれel-get-caskとel-get-cliを使うと同様のことが可能です. (あとで詳しく解説します.)
El-Getでは, Emacs上でM-x command
でパッケージの追加/削除/更新ができます. Caskにこの機能はありません*2. El-GetではM-x
での対話的なインストールは非同期で実行するため, インストール中も作業の手を止める必要がありません.
El-Getはパッケージの配布元のVCSを扱うためのコマンド(git
コマンド等)以外に外部コマンドを必要とせず, Emacsだけあれば最低限動作します. ブートストラップがEmacs Lispで書けるため, Emacs設定ファイルを読み込むだけでEl-Getそのもののインストールまで済ませられます. Caskではなぜかパッケージ管理のコマンドの実行にpython
が必要になります. 実際の処理はEmacs Lispで書かれていますが, コマンドから起動する部分だけPythonで書かれているというおかしな設計です. さらに悪いことに, CaskそのもののインストールをブートストラップするスクリプトもPythonで書かれているので, Emacs設定ファイルでCaskのインストールまで完結させるのは容易ではありません*3.
El-Getは設定ファイル内で必要なパッケージのインストールまで指示することを強く意識しているため, パッケージがインストールされていたら(インストールに成功したら)実行する設定を定義できます. これは別のファイルに分離することも, インストールの指示と同じ場所に書くこともできます. (あとで詳しく解説します.)
Caskの唯一の利点は, パッケージ開発者が開発中のパッケージの依存パッケージを管理したり, (テスト等のために)依存パッケージをロードできる状態の実行環境を整えたりする機能でしょう. Caskは本来その目的のために存在しています. とはいえ, Emacsに元から備わっているしくみを使った実行環境の隔離の方法(あとで詳しく解説します)などを使えばたいていのことは事足りるので, Caskによる支援はそれほど重要ではありません. むしろEmacs Lispによる開発にPythonが必要になる点はパッケージ開発者としては見過ごせません. EmacsでできることはEmacsだけでやるべきです.
El-Getでパッケージ管理
El-Getはただそのまま使ってみることもできますが, ここではEmacsの設定ファイル(~/.emacs.d/init.el
など)に設定を書いて, 複数の環境で設定共有する場合についてとくに解説します.
初期設定
Emacsの設定ファイル(~/.emacs.d/init.el
など)の先頭に以下のように書きます.
(when load-file-name (setq user-emacs-directory (file-name-directory load-file-name))) (add-to-list 'load-path (locate-user-emacs-file "el-get/el-get")) (unless (require 'el-get nil 'noerror) (with-current-buffer (url-retrieve-synchronously "https://raw.githubusercontent.com/dimitri/el-get/master/el-get-install.el") (goto-char (point-max)) (eval-print-last-sexp)))
最初の2行はこの設定ファイルをどこか隔離されたディレクトリに置いて試したい場合にも対応するための設定で, El-Getと直接は関係しません. あとで詳しく触れますが, この設定はEl-Getを使うかどうかによらずやっておくべきです. ただし, この方法ではEmacs設定ファイルをEmacsディレクトリ下に置く必要があるので~/.emacs
や~/.emacs.el
は設定ファイルとして使えません. (~/.emacs
だとなにかよいことがあるわけでもないので~/.emacs.d/init.el
を使うことをおすすめします.)
残りは, El-Getがインストールされていればそれを有効化し, そうでなければGitHubからダウンロードしてインストールするためのものです. El-GetそのものをEmacsの標準機能のみを用いてインストールするので, これを書いてEmacsを起動するだけでEl-Getがインストールされ使える状態になります.
インストールするパッケージを指定
パッケージをインストールするには, Emacs設定ファイル内(のEl-Getの初期設定よりも後)に(el-get-bundle パッケージ名)
と書きます. たとえば, 以下のように書くと, Auto-CompleteとEvilの2つのパッケージがインストールされます.
(el-get-bundle auto-complete) (el-get-bundle evil)
既に前回の実行でパッケージがインストール済みの場合は改めてインストールされたりはしません.
インストール可能なパッケージ(レシピが定義されているパッケージ)を調べたいときは, Emacs上でM-x el-get-list-packages
とするとリストが表示されます*4. これでリストされる以外にもELPAやMELPA, Emacs Wiki等で提供されているパッケージは, それらの配布元タイプを指定することでインストール可能です.
配布元を指定してインストール
配布元をELPA系にしたい場合は, パッケージ名の前にelpa:
をつけます. 使用するリポジトリは, 主要なものは自動的に設定されているのでとくに指定する必要はありません.
(el-get-bundle elpa:undo-tree)
ELPA系で配布されておらず公式レシピにもないものは, 次の節で触れる詳細オプションを通して細かく配布元を指定する必要がありますが, 特定の配布元の場合はごく簡単な指定でインストール可能です. たとえば, Emacs WikiやEmacsmirrorで配布されているパッケージは, それぞれパッケージ名の前にemacswiki:
, emacsmirror:
をつけるだけでインストールできます. 他に, GitHubやGistからの場合もかんたんな記法でインストールできます.
GitHubからインストールしたい場合は所有者/リポジトリ
をパッケージ名の代わりに指定します. たとえば, tarao/el-get-lockをインストールするには以下のようにします.
(el-get-bundle tarao/el-get-lock)
内部的なパッケージ名(リストしたときや削除/更新の際に指定する名前)はリポジトリ名部分になります. もしリポジトリ名とは別のパッケージ名にしたい場合は:name
オプションをつけます. たとえば, tarao/tab-group-elをtab-group
という名前でインストールするには以下のようにします.
(el-get-bundle tarao/tab-group-el :name tab-group)
Gistからインストールしたい場合はgist:Gist ID:パッケージ名
を指定します. たとえば, gist:5019545のevil-tab-page
をインストールするには以下のようにします.
(el-get-bundle gist:5019545:evil-tab-page :depends (evil elscreen))
この場合はevil
およびelscreen
に依存しているので:depends
オプションに指定します.
詳細オプションつきインストール
配布元や依存パッケージなどの細かい制御が必要な場合は詳細オプションを使います. オプションは:
で始まるオプション名とオプション値をパッケージ名に続けて並べることで指定します. たとえば, :type
オプションにgithub
, :pkgname
オプションにtarao/elisp
, :features
オプションにyaicomplete
を指定して, tarao-elisp
パッケージをインストールする場合は以下のように書きます.
(el-get-bundle tarao-elisp :type github :pkgname "tarao/elisp" :features yaicomplete)
使用可能なオプションの完全なリストはel-get.info
の"Authoring Recipes"の章を参照してください(EmacsからはC-u M-x info RET
としたあと~/.emacs.d/el-get/el-get/el-get.info
を指定すると読めます). ここではよく使うものだけ紹介します.
:type
- 配布元の種類を指定します.
el-get/methods
にel-get-種類名.el
のファイルがあるものが利用可能です.:type
を省略した場合, 基本的には定義済みのレシピ(自分で用意していない限りは公式レシピ)に定義された配布元が使用され, 他に指定したオプションは定義済みレシピによる指定を上書きします. 逆に:type
を指定した場合は定義済みレシピがあろうと無視され,:type
と同時に指定したオプションのみが使用されます.:type
を省略した場合で定義済みレシピがない場合でもパッケージ名部分や:url
オプションなどから類推できる場合は:type
が自動設定されます. :url
- 配布元のURLを指定します.
:type
に応じてHTTPやGitプロトコルなどのURLを文字列で(""
で囲って)指定します. :pkgname
- Githubなどの配布元の場合に, その配布元での位置(
"所有者/リポジトリ"
など)を文字列で(""
で囲って)指定します. :depends
- 依存しているパッケージの名前を指定します. 複数ある場合は
()
で囲んで並べます. :features
- パッケージインストール後に
require
したいものを指定します. 複数ある場合は()
で囲んで並べます. :branch
- 配布元がVCSの場合に, 特定のブランチを選択します. 文字列で(
""
で囲って)指定します. :checkout
- 配布元がVCSの場合に, 特定のコミット等を選択します. 文字列で(
""
で囲って)指定します.
パッケージの設定
El-Getをパッケージのインストーラとしてのみ使いパッケージの設定は従来通りに書く, という場合はこの節を読む必要はありません. 設定をパッケージごとにわかりやすく管理したい場合に参考にしてください.
パッケージごとの設定をそれぞれ独立したファイルに書いておきたい場合は, el-get-user-package-directory
変数に設定ファイルの置き場所を指定します. たとえば, ~/.emacs.d/init
に置くのであればEmacs設定ファイルに以下のように書きます.
(setq el-get-user-package-directory (locate-user-emacs-file "init"))
あとは~/.emacs.d/init/init-パッケージ名.el
というファイルを置けば, その名前のパッケージがインストールされているときは自動的にファイルが読み込まれます. このファイルは自動的にバイトコンパイルされます.
el-get-bundle
によるパッケージのインストール指示といっしょに, そのパッケージのための設定を書いておくこともできます. オプション(空でもよい)に続けて任意のLispの式を書くと, そのパッケージがインストールされている時に実行する設定を定義できます. たとえば以下の例では, anything
パッケージをインストール後に(そのパッケージで定義されている)anything-for-files
というコマンドにキーを割り当てます.
(el-get-bundle anything (global-set-key (kbd "C-x b") 'anything-for-files))
設定部分は(初回評価時に)自動的にバイトコンパイルされ, 未定義の変数への参照などは警告されます. これは変数名などの書き間違いを検出できて非常に有用です.
パッケージが自動ロードされるようになっている場合*5は, インストールは完了したもののまだロードされていないタイミングでパッケージで定義された変数にアクセスすると警告されてしまいます. このような場合はwith-eval-after-load
マクロを使って, そのパッケージがロードされてから設定を実行するようにしましょう.
(el-get-bundle anything (global-set-key (kbd "C-x b") 'anything-for-files)) (with-eval-after-load 'anything ;; `anything-map' への参照時には "anything.el" がロード済みであることが必要 (define-key anything-map (kbd "M-n") 'anything-next-source) (define-key anything-map (kbd "M-p") 'anything-previous-source))
ただしこうした場合, with-eval-after-load
内はel-get-bundle
の外なので, 自動的にバイトコンパイルされず警告を最大限に活かせません. かといって, with-eval-after-load
をel-get-bundle
の中に入れてもこの場合はうまくいきません. なぜうまくいかないのか, うまくいくようにするにはどうしたらいいのか知りたければ後の節を参照してください.
その他の構文糖衣
パッケージをインストールしつつ, そのパッケージをすぐさまロードしたい(require
したい)という場合はel-get-bundle!
が使えます.
(el-get-bundle! evil)
上の指定は以下と同じ意味です.
(el-get-bundle evil :features evil)
もし, パッケージ名とロードする機能の名前が異なる場合は, (el-get-bundle! 機能名 in パッケージ名)
記法を使うこともできます.
(el-get-bundle! yaicomplete in tarao/elisp)
上の指定は以下と同じ意味です.
(el-get-bundle elisp :type github :pkgname "tarao/elisp" :features yaicomplete)
require
したいものが複数ある場合は:features
オプションを直接使ってください.
パッケージ管理のワークフロー
パッケージを新しく試す
気になるパッケージを試してみるいちばん簡単な方法は, Emacs上でM-x el-get-install
することです. レシピが定義済みのものに限られますが, これだけでパッケージをインストールできます.
レシピが定義済みでない場合はlisp-interaction-mode
になっているバッファ(たとえば*scratch*
バッファ)で, el-get-bundle
によるインストール指定を書いて, 指定の末尾でC-x C-e
するとインストールできます. その他, どういう設定をしたらいいか模索するときも同じように設定を書いてC-x C-e
してみるとよいでしょう(エラーが出たらq
キーでエラーバッファを閉じることができます).
ひととおり試して常用したいとおもったら, Emacs設定ファイルにel-get-bundle
や設定の行を追加しましょう.
パッケージをアンインストール
試してみたところやっぱり必要ないとおもったら, M-x el-get-remove
でパッケージを削除できます. アンインストールしてもいちど読み込んだ機能が元に戻るわけではないので, M-x unload-feature
するか, Emacsを再起動するかしましょう.
自動的にインストールされた依存ファイルもきちんと消したい場合は, インストールする前に未インストールの依存パッケージを調べておいて, 試した後にそれらもいっしょに消すことになりますが, 通常そこまでする必要はありません. もし, el-get-bundle
されたもの以外を綺麗に消したい場合は, いったん~/.emacs.d/el-get
をまるごと削除して, Emacsを再起動して全パッケージをインストールしなおすのが確実です. 後の節で触れる環境隔離の方法を使って, 常用しているのとは別の汚してもかまわない環境で試すようにするのもよいでしょう.
既存のパッケージを更新
パッケージの更新はM-x el-get-update
でできます. 全パッケージを更新する場合はM-x el-get-upadate-all
します.
パッケージリストを見ながら操作
インストール/更新/削除の操作はM-x el-get-list-packages
して表示されるリストからもできます. 操作キーを調べるにはリストのバッファ上でh
キーを押しましょう.
バージョンの固定
配布元がVCSの場合は:checkout
オプションで任意のバージョンに固定することができますが, 各パッケージのインストール指定にいちいちバージョンを書いていくのはあまりにたいへんです.
そもそも, パッケージのバージョンを固定したい理由は, きちんと動くことがわかっているバージョンをインストールしたい, 勝手に最新版になることでパッケージおよび設定間の互換性が崩れて動かなくなるのを避けたい, ということだとおもいます. つまり, 本当に必要なのは指定したバージョンに固定することではなく, きちんと動くことを確かめたときのスナップショットを取ることのはずです.
el-get-lockを使うと, インストールされているパッケージのバージョンを~/.emacs.d/el-get.lock
に書き出し, その情報を元にインストールされるバージョンを制限できます. このファイルのバックアップをとることでスナップショットとしての役割を果たすわけです.
設定
バージョン固定を有効にするにはEmacs設定ファイルに以下のように書きます.
(el-get-bundle tarao/el-get-lock) (el-get-lock)
特定のパッケージだけ固定の対象にするには, el-get-lock
に複数の引数を与えて, 固定するパッケージを指定します(呼出しそのものを複数回に分けても構いません).
(el-get-lock 'evil 'anything) (el-get-lock 'magit)
逆に, 特定のパッケージのみ固定の対象から外すには, いったん無引数のel-get-lock
で全パッケージを対象にしたあと, 外すパッケージをel-get-lock-unlock
で指定します.
(el-get-lock) (el-get-lock-unlock 'undo-tree)
ロックファイルの生成
バージョン固定の対象となったパッケージが新規にインストールされると, ~/.emacs.d/el-get.lock
にインストールされたバージョンが書き出され, 以降の再インストール時にはこのバージョンに固定されます. もし既にel-get-bundle
(あるいは他のEl-Getによるインストール方法)を使用していて, インストール済みのパッケージがある場合は, バージョンを書き出すためにいったんインストールしなおす必要があります. M-x el-get-reinstall
かM-x el-get-update
(1つのパッケージのみを対象とする場合), あるいはM-x el-get-update-all
(全パッケージを対象とする場合)するとよいでしょう.
書き出されたel-get.lock
は, ~/.emacs.d/init.el
などとともにVCSで管理しておくことをおすすめします. たとえばGitで管理する場合であれば, 以下のようにしておきます.
cd ~/.emacs.d/ git add el-get.lock git commit -m 'Add el-get lock file.'
固定されたバージョンを別環境でインストール
~/.emacs.d
そのものをVCSで管理するなどして, 別環境に持ち出した場合は, 新たにEl-Getで環境構築する際に自動的にバージョン固定されます. つまりEmacsを起動するだけでうまくいきます.
既にEl-Getを使っている別環境にel-get-lock
の設定とel-get.lock
を新たに持ち込んだ場合は, M-x el-get-lock-checkout
(パッケージを指定せずにそのまま確定)で全パッケージのバージョンを固定してインストールしなおすことができます.
最新版に更新
バージョンが固定されたパッケージを最新バージョンに更新する場合は, 単にM-x el-get-update
するだけです. 更新されるとel-get.lock
の該当パッケージの箇所が変更されているので, 更新したパッケージがきちんと動作していそうなことを確認したのち, VCSに反映しておきましょう. 以下はGitの例です.
cd ~/.emacs.d/ git add el-get.lcok git commit -m 'Update some packages.'
更新を元に戻す
パッケージを更新してみたら既存の設定を壊してしまうことがわかったとしましょう. 元に戻すには次のようにします.
el-get.lock
に記載されているバージョンを動いていたときのものに戻すM-x el-get-lock-checkout
で該当するパッケージをel-get.lock
のバージョンに戻す
たとえば, el-get.lock
をGitで管理していて, 最新コミットが動いていたときのバージョンになっている場合は, 以下のようにすることでel-get.lock
を元に戻せます.
cd ~/.emacs.d/ git checkout el-get.lock
ただし, このやり方はel-get.lock
のすべての変更を元に戻してしまうので, 複数のパッケージをいちどに更新した場合は差分を見ながら手動でel-get.lock
を編集する必要があるかもしれません.
el-get.lock
さえ元に戻れば, あとはM-x el-get-lock-checkout
でパッケージのバージョンを戻せます.
サポートされる配布元
el-get-lockはすべての配布元のバージョン固定をサポートしているわけではありません. バージョンの概念のない配布元や, 前のバージョンをインストールする方法が提供されていない場合は固定することができません.
- 完全サポート
git
,github
,emacsmirror
,hg
- これらの配布元のパッケージのバージョンは固定, 更新でき, 元に戻せます.
- 部分サポート
http
,ftp
,emacswiki
- これらの配布元のパッケージのバージョンは固定, 更新できますが, 元に戻すことはできません. もしロックファイルを利用した新規インストール時により新しいバージョンしかなかった場合はエラーになります. この場合は
M-x el-get-reinstall
でそのパッケージを強制的に最新バージョンに固定しなおすしかありません.
ELPA系は, package.el
のしくみ上バージョンを戻せないので, VCSで配布されているパッケージはVCSでインストールすることを強くおすすめします. 安定版を使いたいという場合でも, 配布元が安定版のブランチ(あるいはタグ)を用意していればそれを:branch
オプションで指定するのがよいでしょう.
その他のTips
隔離された環境にパッケージをインストール
El-Getの導入の節で触れたように, Emacs設定ファイルを適切に書いておくと, インストールされるパッケージを含むEmacsの全設定を特定のディレクトリ下に隔離できます.
(when load-file-name (setq user-emacs-directory (file-name-directory load-file-name)))
先頭にこのように記述したEmacs設定ファイルを, たとえば~/path/to/somewhere/init.el
に保存したとすると, 以下のようにしてすべて~/path/to/somewhere
内で完結した状態のEmacsを起動できます*6.
emacs -q -l ~/path/to/somewhere/init.el
こうすることで, 既存の設定に干渉することなく, 新しいパッケージやその設定をクリーンな環境で試すことができます. このように記述されたEmacs設定ファイルが広まれば, 他の人の設定を試しに使ってみるのも非常にかんたんになります.
パッケージのインストール先をEmacsバージョンによって変える
インストールしたパッケージは通常バイトコンパイルされますが, 異なるバージョンのEmacsでコンパイルされたバイトコードは基本的に互換性がありません*7. もし複数のバージョンのEmacsで設定ファイルを共有したい場合には, パッケージをインストールするディレクトリをEmacsのバージョンごとに別々にするとよいでしょう.
El-Getでインストールするパッケージは, ELPA系を除いてデフォルトでは~/.emacs.d/el-get
以下に, ELPA系はpackage.el
によってインストールされ, デフォルトでは~/.emacs.d/elpa
に保存されます. それぞれel-get-dir
とpackage-user-dir
で変更可能です. Emacsのバージョン文字列はemacs-version
変数に入っているので, この文字列でディレクトリを作るとよいでしょう.
たとえば, Emacsのバージョンが24.4.1
の場合, 以下のようにするとそれぞれ~/.emacs.d/24.4.1/el-get
, ~/.emacs.d/24.4.1/elpa
にインストールされるようになります.
(let ((versioned-dir (locate-user-emacs-file emacs-version))) (setq el-get-dir (expand-file-name "el-get" versioned-dir) package-user-dir (expand-file-name "elpa" versioned-dir)))
この設定は, user-emacs-directory
の設定よりも後, El-Getそのもののインストールよりも前の位置に書く必要があります.
設定のバイトコンパイル
次の例で, anything-map
はanything.el
で定義されており, anything.el
は自動ロードされるようになっているとしましょう.
(el-get-bundle anything (global-set-key (kbd "C-x b") 'anything-for-files) (with-eval-after-load 'anything (define-key anything-map (kbd "M-n") 'anything-next-source) (define-key anything-map (kbd "M-p") 'anything-previous-source)))
el-get-bundle
の中の設定は, anything.el
がインストールされた上で実行されることは保証されますが, この設定が実行されるときにはまだanything.el
はロードされていません. したがって, anything-map
は未定義の変数ということになり, 設定部分のコンパイル時に警告が出ます.
...init-1_anything.el:5:15:Warning: reference to free variable `anything-map'
では, anything-map
を参照する前にrequire
したらどうでしょうか?
(el-get-bundle anything (global-set-key (kbd "C-x b") 'anything-for-files) (require 'anything) ; 常にanythingをロード (with-eval-after-load 'anything (define-key anything-map (kbd "M-n") 'anything-next-source) (define-key anything-map (kbd "M-p") 'anything-previous-source)))
たしかにこれで警告は出なくなりますが, anything
が常にロードされてしまいます. せっかく自動ロードが設定されているのだから, 必要になるまでロードを遅らせてEmacsの起動を速くしたいところです.
コンパイル時には変数の参照が正しいかどうか調べるためにanything
がロードされている必要があるので, 最初の一回のrequire
はどうしても必要です. しかし一度コンパイルしたあとはwith-eval-after-load
によってanything
の読み込み後に実行されることが保証されて, コンパイル処理も走らないので変数の参照はチェックされません(チェック済みという扱いです). つまり既にコンパイル済みだったらrequire
しないようにできればよさそうです.
実はまさにこれをやってくれるパッケージが存在します*8. tarao/with-eval-after-load-feature-elです.
(el-get-bundle tarao/with-eval-after-load-feature-el) (el-get-bundle anything (global-set-key (kbd "C-x b") 'anything-for-files) (with-eval-after-load-feature 'anything (define-key anything-map (kbd "M-n") 'anything-next-source) (define-key anything-map (kbd "M-p") 'anything-previous-source)))
with-eval-after-load-feature
は, コンパイル時には指定されたパッケージをrequire
してから中身をコンパイル, 実行時はwith-eval-after-load
と同じように振る舞うということをしてくれます.
Caskエミュレーション
すでに見たように, Caskの機能のうち自分のEmacsで使うパッケージを管理するためのしくみは, ほぼEl-Getによって網羅されていると言ってさしつかえありません*9. この節ではもう一歩踏み込んで, Caskを使っているユーザがスムーズにEl-Getに移行するための方法を紹介します.
CaskファイルをEl-Get形式に書き換え
ここまでの説明を読んでいればCask
ファイルをEl-Get用に書き換えるのはかんたんでしょう. とくにオプションのないdepends-on
は, パッケージ名の前にelpa:
をつけたel-get-bundle
に, :git
のような指定は:type
と:url
つきのものに置き換えるだけです.
たとえば, id:naoyaさんのCask - naoyaのはてなダイアリーの記事に載っているCask
ファイルの場合は, 以下のように書き換えることで全く同じパッケージをインストールできます.
(el-get-bundle elpa:ag) (el-get-bundle elpa:anything) (el-get-bundle elpa:auto-complete) (el-get-bundle elpa:browse-kill-ring) (el-get-bundle elpa:color-theme) (el-get-bundle elscreen :type git :url "git@github.com:knu/elscreen.git") (el-get-bundle elpa:flycheck) (el-get-bundle elpa:git-gutter) (el-get-bundle elpa:pbcopy) (el-get-bundle elpa:popup) (el-get-bundle elpa:popwin) (el-get-bundle elpa:powerline) (el-get-bundle elpa:quickrun) (el-get-bundle elpa:recentf-ext) (el-get-bundle elpa:zlc) ;; prog modes (el-get-bundle elpa:coffee-mode) (el-get-bundle elpa:go-mode) (el-get-bundle elpa:js2-mode) (el-get-bundle elpa:json-mode) (el-get-bundle elpa:less-css-mode) (el-get-bundle elpa:motion-mode) (el-get-bundle elpa:puppet-mode) (el-get-bundle elpa:rhtml-mode) (el-get-bundle elpa:ruby-mode) (el-get-bundle elpa:sass-mode) (el-get-bundle elpa:slim-mode) (el-get-bundle elpa:rubocop) (el-get-bundle elpa:ruby-block) (el-get-bundle elpa:ruby-electric) (el-get-bundle elpa:ruby-end) (el-get-bundle elpa:go-autocomplete)
これをたとえばCask2ElGet
というファイル名で保存して, El-Getの初期設定の後に以下のように書けばでき上がりです.
(load (locate-emacs-file "Cask2ElGet"))
非常にかんたんそうですが, 一つ落とし穴があります. El-GetはELPA系ではなくVCSからのインストールを基本としていて, 依存ファイルはレシピできちんと指定するのが原則のため, このままではインストールされたパッケージの依存パッケージ(のうち上に列挙されていないもの)が正常に読み込めません(インストール自体はされます).
もし, ELPA系の安定版にこだわらないなら, 上記からelpa:
を除いたものを用いると最新版がきちんとインストールされ, 依存パッケージも問題なく読み込めます. 公式レシピのみで足りるので自分でレシピを定義する必要もありません.
(el-get-bundle ag) (el-get-bundle anything) (el-get-bundle auto-complete) (el-get-bundle browse-kill-ring) (el-get-bundle color-theme) (el-get-bundle elscreen :type git :url "git@github.com:knu/elscreen.git") (el-get-bundle flycheck) (el-get-bundle git-gutter) (el-get-bundle pbcopy) (el-get-bundle popup) (el-get-bundle popwin) (el-get-bundle powerline) (el-get-bundle quickrun) (el-get-bundle recentf-ext) (el-get-bundle zlc) ;; prog modes (el-get-bundle coffee-mode) (el-get-bundle go-mode) (el-get-bundle js2-mode) (el-get-bundle json-mode) (el-get-bundle less-css-mode) (el-get-bundle motion-mode) (el-get-bundle puppet-mode) (el-get-bundle rhtml-mode) (el-get-bundle ruby-mode) (el-get-bundle sass-mode) (el-get-bundle slim-mode) (el-get-bundle rubocop) (el-get-bundle ruby-block) (el-get-bundle ruby-electric) (el-get-bundle ruby-end) (el-get-bundle go-autocomplete)
安定版に対するこだわりという点に関しては, 個人的にはあまりうまい考えだとは思いません. ELPA系で提供されるものであってもリポジトリによっては安定版を提供しているとも限らず, また, パッケージ提供元の考える「安定版」よりも, 自分のEmacs(とその設定)できちんと動くことがわかっているバージョンを使うことの方が重要だと思うからです.
とはいえ, これをCaskからEl-Getへのスムーズな移行と言うには無理があるかもしれません. そこで, Cask
ファイルをそのままEl-Getで読めるようにする方法を用意しました.
Caskファイルをそのまま読む
tarao/el-get-caskを使うと, Cask
ファイルをそのままEl-Getで使えて, デフォルトではELPA系のパッケージがインストールされ依存パッケージの読み込みも解決されます. (もちろんCaskのインストールは必要ありません.)
使い方もいたってかんたんで, El-Getの初期設定の後に以下のように書くだけです.
(el-get-bundle tarao/el-get-cask) (el-get-cask-load)
さらに, Cask
ファイル中に以下のように書いておくと, ELPA系ではなくEl-Getのレシピを優先するようになります.
(source el-get)
こうすれば知らないうちにEl-Get流のやり方に置き換わっているというわけです.
実はel-get-caskではdepends-on
はel-get-bundle
の別名のように解釈されるので, el-get-bundle
での配布元の簡易記法や, El-Getのすべての詳細オプションもサポートされます.
コマンドライン
Caskはコマンドラインでパッケージのインストールができる点が嬉しかった, という人にはtarao/el-get-cliをおすすめします. 設定ファイル(~/.emacs.d/init.el
)をロードしたEmacsをバッチモードで起動してEl-Getのコマンドを実行するという単純な作りで実現しているので, 基本的にM-x
でEl-Getのコマンドを実行するのと同様に使えます.
利用例
El-Getを実際に使っている設定ファイルの例として, 筆者のEmacs設定ファイルを挙げておきます.
この設定では, el-get-bundle
の別名として定義したbundle
というマクロ*10を使って, パッケージのインストールとその設定を同じ箇所に書くようにしています. 設定ファイルは内容ごとに適度にファイルに分割したものをシンボリックリンクで順序づけて, init-loaderで読み込むようになっています. 公式レシピ以外に自分で定義したレシピも使っています.
おわりに
El-Getは, 自分のEmacsで使うパッケージを管理する機能としてはpackage.el
やCaskの機能を上回るものだということを紹介しました. 実はEl-Getの方がCaskより歴史が古いようなのでこれまでの蓄積によるものとも言えますが, El-Getの方がEmacs本来のやり方に沿って実装されていて他の開発者が加わって拡張していきやすいというのもあるかもしれません.
謝辞
Caskに対する不満を共有してくれた同僚のid:shiba_yu36さんとid:mechairoiさんに感謝します. 彼らの話がこの記事を書く直接のきっかけとなりました.
*1:ELPA(Emacs公式)の他にMELPA, marmalade, SCなどがあります.
*2:Palletというパッケージを使うとできますが最低限のコマンドしかなく, たとえばパッケージの削除はできません.
*3:実際にはgit cloneするだけでよさそうなので全く不可能なわけではありませんが, 不親切なことは間違いありません.
*4:package.elにおけるpackage-list-packagesのようなものです.
*5:なにかのコマンドを実際に使うときまでそのパッケージを使う必要がない(最初から有効にして常に何かを表示/実行しておくような類のもの以外の)場合は常にそうなっているべきです.
*6:稀にこの設定を無視して~/.emacs.dに決め打ちして動作する行儀の悪いパッケージが存在しますが, そのような挙動はバグと言ってよいのでパッケージ開発者に文句を言いましょう.
*7:"In general, any version of Emacs can run byte-compiled code produced by recent earlier versions of Emacs, but the reverse is not true. "
*8:この目的のために筆者が作りました.
*9:パッケージ開発者向けの機能はこの限りではありません.