背景
これまで様々なパッケージマネージャが登場していましたが、現在use-package
を除くとpackage.el
とstraight.elが人気のようです。
Emacs User Survey — 2022
Emacsパッケージマネージャ決定版:これからのパッケージ管理はstraight.elで決まり!
私も以下の点からstraight.elを愛用していました。
-
package.el
は一度インストールしたパッケージは、ちゃんと削除しないとロードされてしまう。 - melpaがたまに壊れていることがあり、アップデートを行うとEmacsが動かなくなることがある。
しかし、straight.elはインストールとアップデートに時間がかかり、またWindowsだとstraight-pull-all
のあと自動でビルドしてくれないのが不満に感じていました。
このpackage.el
とstraight.elの不満を解消する方法はないかと思っていたところ、elpacaというパッケージを見つけました。
elpacaとは
elpacaはstraight.elと同様にgit clone
でパッケージをインストール・ビルドを行いますが、非同期で処理することで高速にインストールされるようになっています。
straight.elよりも高速にインストール・更新が可能になっています。
【Windows】シンボリックリンクを有効にする
Elpacaではシンボリックリンクが必須となっていますので、下記の方法を使用する必要があります。
方法1:開発者モードを利用する
Emacs30を利用すると特別な設定もなく開発者モードを有効にするだけでシンボリックリンクが使えましたので、私はEmacs30+開発者モードにしています。
Scoopのインストール
$ Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
$ irm get.scoop.sh | iex
Emacs30のインストール(Emacs Wiki)
$ scoop bucket add kiennq-scoop https://github.com/kiennq/scoop-misc
$ scoop install emacs-k
方法2:管理者権限で実行する
Windowsでシンボリックリンク有効にするには管理者権限で実行する必要があります。
アプリケーションを管理者権限で実行する方法(Windows 10/8/7/Vista)
elpacaのインストール
下記のコードをinit.el
に追加して、elpacaをインストールします。
(defvar elpaca-installer-version 0.5)
(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory))
(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory))
(defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory))
(defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git"
:ref nil
:files (:defaults (:exclude "extensions"))
:build (:not elpaca--activate-package)))
(let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory))
(build (expand-file-name "elpaca/" elpaca-builds-directory))
(order (cdr elpaca-order))
(default-directory repo))
(add-to-list 'load-path (if (file-exists-p build) build repo))
(unless (file-exists-p repo)
(make-directory repo t)
(when (< emacs-major-version 28) (require 'subr-x))
(condition-case-unless-debug err
(if-let ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*"))
((zerop (call-process "git" nil buffer t "clone"
(plist-get order :repo) repo)))
((zerop (call-process "git" nil buffer t "checkout"
(or (plist-get order :ref) "--"))))
(emacs (concat invocation-directory invocation-name))
((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch"
"--eval" "(byte-recompile-directory \".\" 0 'force)")))
((require 'elpaca))
((elpaca-generate-autoloads "elpaca" repo)))
(progn (message "%s" (buffer-string)) (kill-buffer buffer))
(error "%s" (with-current-buffer buffer (buffer-string))))
((error) (warn "%s" err) (delete-directory repo 'recursive))))
(unless (require 'elpaca-autoloads nil t)
(require 'elpaca)
(elpaca-generate-autoloads "elpaca" repo)
(load "./elpaca-autoloads")))
(add-hook 'after-init-hook #'elpaca-process-queues)
(elpaca `(,@elpaca-order))
また一度に多くのパッケージをインストール・アップデートする場合、too many open files
というエラーが出る可能性が高いので、下記のコードを追加しキューのリミットを設定しておきます。
※端末のスペックに応じて変更してください)
(setq elpaca-queue-limit 15)
パッケージのインストール
elpacaはqueue
に溜まったパッケージを非同期にインストール・ロードしていきます。
下記のように記述するとqueue
にパッケージや処理が溜まっていきます。
(elpaca org)
(elpaca xxx
(setq xxx-yyy nil)
(hogehoge)
(fogafoga))
また、use-package
も下記のようにインストールすることで利用できるようになります。
;; Install use-package support
(elpaca elpaca-use-package
;; Enable :elpaca use-package keyword.
(elpaca-use-package-mode)
;; Assume :elpaca t unless otherwise specified.
(setq elpaca-use-package-by-default t))
elpaca-wait
elpaca-wait
はパッケージのインストール・ロードが完了するまで、処理を待ってくれます。use-package
等、init.el
の構成に使用しているパッケージはelpaca-wait
を使用してあらかじめインストールしておきます。
;; Install use-package support
(elpaca elpaca-use-package
;; Enable :elpaca use-package keyword.
(elpaca-use-package-mode)
;; Assume :elpaca t unless otherwise specified.
(setq elpaca-use-package-by-default t))
(elpaca general)
;; Block until current queue processed.
(elpaca-wait)
elpaca-process-queues
elpaca-process-queues
はqueueに溜まっているパッケージの設定を非同期に実行してくれるコマンドです。公式では下記のようにafter-init-hook
のあとに実行するようになっています。
(add-hook 'after-init-hook #'elpaca-process-queues)
しかし、dashboard
等のパッケージを利用している場合、上記のタイミングだと都合が悪い場合があるので、私はinit.el
の末尾に直接elpaca-process-queues
を呼び出しています。
;;; package --- Summary
;;; Commentary:
;;; Code:
;; 1.elpacaのインストール
;; 2.init.elの設定に使用するパッケージ
(elpaca-wait)
;; 3.各設定の記述
(use-package hogehoge)
(use-package fugafuga
;; 3で記述された処理を非同期に実行
(elpaca-process-queues)
(provide 'init)
;;; init.el ends here
設定例
;;; package --- Summary
;;; Commentary:
;;; Code:
(defvar elpaca-installer-version 0.5)
(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory))
(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory))
(defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory))
(defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git"
:ref nil
:files (:defaults (:exclude "extensions"))
:build (:not elpaca--activate-package)))
(let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory))
(build (expand-file-name "elpaca/" elpaca-builds-directory))
(order (cdr elpaca-order))
(default-directory repo))
(add-to-list 'load-path (if (file-exists-p build) build repo))
(unless (file-exists-p repo)
(make-directory repo t)
(when (< emacs-major-version 28) (require 'subr-x))
(condition-case-unless-debug err
(if-let ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*"))
((zerop (call-process "git" nil buffer t "clone"
(plist-get order :repo) repo)))
((zerop (call-process "git" nil buffer t "checkout"
(or (plist-get order :ref) "--"))))
(emacs (concat invocation-directory invocation-name))
((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch"
"--eval" "(byte-recompile-directory \".\" 0 'force)")))
((require 'elpaca))
((elpaca-generate-autoloads "elpaca" repo)))
(progn (message "%s" (buffer-string)) (kill-buffer buffer))
(error "%s" (with-current-buffer buffer (buffer-string))))
((error) (warn "%s" err) (delete-directory repo 'recursive))))
(unless (require 'elpaca-autoloads nil t)
(require 'elpaca)
(elpaca-generate-autoloads "elpaca" repo)
(load "./elpaca-autoloads")))
;; init.elの最後に実行
;; (add-hook 'after-init-hook #'elpaca-process-queues)
(elpaca `(,@elpaca-order))
;; queueの上限設定
(setq elpaca-queue-limit 15)
;; use-packageなどの設定に使用するパッケージを記述
(elpaca elpaca-use-package
(elpaca-use-package-mode)
(setq elpaca-use-package-by-default t))
(elpaca general)
;; 設定に利用するパッケージをセットアップ
(elpaca-wait)
;;各パッケージの設定を記述
(use-package evil
:config
(evil-mode +1))
(use-package vertico
:config
(vertico-mode +1))
;; 設定を非同期でロード
(elpaca-process-queues)
(provide 'init)
;;; init.el ends here
実際の様子
elpacaはUIも提供してくれているので、進捗状況を一覧で確認することができます。
注意点
【2023/03/30】init.elをバイトコンパイルしてinit.elc
の状態でelpacaを呼び出すとエラーがでます。修正されるまでバイトコンパイルせずに運用したほうが良さそうです。
所感
最初はエラーが色々出ていたり、導入に手こずっていましたが、
慣れると非常に快適になりました。
開発が活発に行われているので、今後更に機能が改善されることを楽しみにしています。