読者です 読者をやめる 読者になる 読者になる

package.elにpackage.el以外の方法でインストールした拡張を認識させる

Emacsの拡張の管理は、できるだけpackage.elで行うようにしています。
ある日Emacsを起動すると、以下の警告が表示されました。

Warning (emacs): Unable to activate package `popup'.
Required package `cl-lib-0.3' is unavailable

原因はすぐわかりました。
package.elがインストールしたcl-libをアンインストールしていたためです。

なぜアンインストールしたのかと言うと

現在、私はWindowsUbuntuを併用しており、EmacsのバージョンがWindowsでは24.2.1、Ubuntuでは24.3.1です。
~/.emacs.d を同期させて同じ環境にしています。

で、以前UbuntuEmacsを使った時にエラーに遭遇しまして、その原因がpackage.elがインストールしたcl-libでした。
以下の事例と全く同じ現象で、package.elでcl-libをアンインストールすることにより解決しました。

https://github.com/emacs-jp/issues/issues/13

24.2.1でエラーにならないように

cl-libが自動でインストールされたのは、popup.elが要求しているバージョンが Emacs24.2.1には無いためだったので、ただアンインストールしただけだと、Emacs24.2.1でpopup.elが使えません。

そのため、WindowsEmacsのsite-lispフォルダに、自動でインストールされたcl-libをコピーしておくことにしました。
これで、package.elでcl-libをアンインストールしても、Windowsでも正常に動作するだろうと考えました。

前置き終了

前置きが長くなりましたが、そういう経緯があった上で上記の警告が出たため、
package.elは、自身の管理下でインストールされたもの以外は認識していなそうだと感じました。

警告が出るだけでなく、popup.elを使った機能が正常に動作しなかったので、
何とかしなければいけませんでしたが、package.elでインストールすればUbuntuではまたエラー...。

しょうがないのでソース見る

package.elの処理を見ると、package-initializeされた際、package-user-dirに見つからない拡張は
package--builtinsという変数に定義されていればbuiltinな拡張、でなければ上記警告、という処理でした。

package--builtins

この変数はfinder-inf.elにハードコーディングで定義されていました。
さらに調べてみると、finder-inf.elはfinder.elのfinder-compile-keywords関数が生成したものだとわかりました。
ファイルの更新日時から考えて、Emacsのインストール時に一度だけ実行されているようでした。

finder-compile-keywordsを再実行すれば良さそうでしたが、finder-inf.elが変更されることや、
今後、別の拡張で同じ事象があった時に面倒かなと思ったので、別途実装することにしました。

結論

以下の設定をpackage-initializeの前に記述することで警告を回避できました。

(when (require 'finder nil t)
  (let* ((regist (lambda (file)
                   (with-temp-buffer
                     (insert-file-contents file)
                     (let* ((pkgnm (lm-get-package-name))
                            (pkg (when (and (stringp pkgnm)
                                            (string-match "\\.el\\'" pkgnm))
                                   (intern (replace-regexp-in-string "\\.el\\'" "" pkgnm))))
                            (ver (ignore-errors (version-to-list (lm-header "version"))))
                            (doc (lm-synopsis))
                            (vec (vector ver nil doc)))
                       (when (and pkg
                                  (not (assq pkg package--builtins)))
                         (message "registed package--builtin: %s (%s) --- %s" pkg ver doc)
                         (push `(,pkg . ,vec) package--builtins))))))
         (seek (lambda (dir)
                 (setq dir (expand-file-name dir))
                 (message "seek builtin from %s ..." dir)
                 (dolist (node (directory-files dir))
                   (let ((fullpath (concat dir "/" node)))
                     (cond ((string-match "\\`[._]" node)
                            nil)
                           ((file-directory-p fullpath)
                            (funcall seek fullpath))
                           ((and (file-regular-p fullpath)
                                 (string-match "\\.el\\'" node))
                            (funcall regist fullpath))))))))
    ;; 追加認識させたい拡張があるルートディレクトリ毎に繰り返し記述
    (funcall seek (concat exec-directory "../site-lisp"))
    (funcall seek (concat user-emacs-directory "elisp/etc"))
    ...
    ))

Emacs起動後、*Messages*バッファに以下のようなメッセージがあれば追加できていると思います。

registed package--builtin: cl-lib ((0 4)) --- Properly prefixed CL functions and macros

※ メッセージがうざかったら、(message "... ) の処理を削除して下さい。
※ package--builtins変数を参照する拡張の動作がおかしくなる可能性がありますが、とりあえず様子見という感じで。