如何自动(重新)编译ELPA包?

14

我现在正在尽可能通过 MELPA 和 Marmalade 安装插件,同时使用 git 管理我的 ~/.emacs.d。但是,我已经将 *.elc 文件添加到了 git 的忽略列表中。

这意味着当我在一个系统上安装一个插件,然后开始在另一个系统上使用时,git pull 命令只会拉取 *.el 文件。然而,使用这些文件往往比使用 *.elc 文件要慢。

我尝试将以下内容添加到 ~/.emacs.d/init.el 文件中:

;; load the packages we've installed. Note that package-install
;; byte-compiles the packages, but .elc is ignored by git so we force recompilation here
(byte-recompile-directory (expand-file-name "~/.emacs.d/elpa") 0)
(package-initialize)

很遗憾,这与package.el所做的编译不相等。例如,如果我安装了emacs-eclim,package.el不会编译emacs-eclim/company-emacs-eclim.el,我会收到以下错误:

Leaving directory `/home/wilfred/.emacs.d/elpa'

Compiling file /home/wilfred/.emacs.d/elpa/emacs-eclim-20130310.1237/company-emacs-eclim.el at Mon Mar 11 15:40:01 2013
Entering directory `/home/wilfred/.emacs.d/elpa/emacs-eclim-20130310.1237/'
company-emacs-eclim.el:35:1:Error: Cannot open load file: eclim
Warning: reference to free variable `multiple-cursors-mode'
Warning: reference to free variable `mc--read-char'
Warning: assignment to free variable `mc--read-char'
Warning: reference to free variable `multiple-cursors-mode'
Warning: reference to free variable `mc--read-quoted-char'
Warning: assignment to free variable `mc--read-quoted-char'
Warning: reference to free variable `rectangular-region-mode'
Warning: reference to free variable `rectangular-region-mode'

我该如何让Emacs只编译与package.el相同的文件?

6个回答

10

package.el实际上使用的是完全相同的方法,只是在启动时重新编译使错误更加明显。

所使用的函数是package--make-autoloads-and-compile,它调用了:

(byte-recompile-directory pkg-dir 0 t)

所以问题中的原始代码是正确的。然而,如果要重新编译尚未编译的目录,可以执行以下操作:

(require 'dash)
(require 'f)

(defun was-compiled-p (path)
  "Does the directory at PATH contain any .elc files?"
  (--any-p (f-ext? it "elc") (f-files path)))

(defun ensure-packages-compiled ()
  "If any packages installed with package.el aren't compiled yet, compile them."
  (--each (f-directories package-user-dir)
    (unless (was-compiled-p it)
      (byte-recompile-directory it 0))))

(ensure-packages-compiled)

8

我建议不要将软件包放入版本控制中(你也不会将.o文件放在版本控制下,对吧?)。以下是我用来同步软件包的代码:

(setq jpk-packages
      '(
        ac-dabbrev
        ...
        yasnippet
        ))

(package-initialize)
(add-to-list 'package-archives
             '("melpa" . "http://melpa.milkbox.net/packages/"))
(add-to-list 'package-archives
             '("org" . "http://orgmode.org/elpa/"))

(when (not package-archive-contents)
  (package-refresh-contents))

(dolist (pkg jpk-packages)
  (when (and (not (package-installed-p pkg))
           (assoc pkg package-archive-contents))
    (package-install pkg)))

(defun package-list-unaccounted-packages ()
  "Like `package-list-packages', but shows only the packages that
  are installed and are not in `jpk-packages'.  Useful for
  cleaning out unwanted packages."
  (interactive)
  (package-show-package-list
   (remove-if-not (lambda (x) (and (not (memq x jpk-packages))
                            (not (package-built-in-p x))
                            (package-installed-p x)))
                  (mapcar 'car package-archive-contents))))

我将以上内容放在init.el中(当然,该文件已经被版本控制),它会在emacs启动时安装任何不存在的包。更新是从缓冲区package-list-packages创建的。 package-list-unaccounted-packages显示所有已安装但不在我的jpk-packages列表中的包,并且很容易删除我从列表中删除的那些包。
回答您的具体问题,我只需删除elpa目录并重新安装所有内容(使用上述代码)。

看起来你的 jpk-packages 相当于 Carton manifest(就像 @francesco 上面用 Pallet 描述的那样),但是你失去了指定确切包版本的能力。 - Wilfred Hughes
我不使用Carton或Pallet,但据我所知,我使用的是它们非常简单的版本。我喜欢我的方式,因为我几乎需要所有的代码来引导carton/pallet,到目前为止,我没有遇到未指定软件包版本的问题。 - jpkotta
4
你不会把.o文件放在版本控制下,对吧?我认为这个比较不公平。构建.o文件应该是可再生的。然而,似乎没有一种方法来指定特定版本的包。我检查打包文件,以便能够部署一个相互兼容且可正常工作的emacs配置的一致快照。 - Greg Sexton

6
我喜欢的解决“如何自动重新编译任何过时的.elc文件”的更一般的问题的方法是使用https://github.com/tarsius/auto-compile
我曾经有过一些自定义解决方案,涵盖了我遇到的大多数情况,但这个库涵盖了我所做的一切,并且还有更多。只需在加载任何其他内容之前初始化它,就可以解决问题。
至于最初的问题,即不编译原始未编译的文件,这就是byte-recompile-directory中的0参数所做的,因此您明确要求该行为。默认行为是仅重新编译现有过时的.elc文件,因此应该删除该0

2

我不知道如何像package.el一样精确地对字节编译软件包的问题进行回答。

然而,针对您在通过Melpa或marmalade安装扩展时维护emacs配置目录的更一般性问题,我建议您查看pallet,这个工具旨在解决这个问题。


1
谢谢,我不知道Pallet。然而,我真的希望我的.emacs.d是自包含的 - 我曾经因为很少更新的elisp文件不再在我最初下载它们的地方而感到痛苦。 - Wilfred Hughes

2

1
您的问题很简单,就是在初始化包之前对文件进行了字节编译。如果您交换这两行代码,您的问题应该会消失。您收到的错误信息(“Cannot open load file: eclim”)是因为“~/.emacs.d/elpa/emacs-eclim-20130310.1237/”尚未在您的“load-path”中,并且“package-initialize”将把它添加到您的“load-path”中(以及其他一些东西)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接