使用 MELPA 自动安装 Emacs packages

32

我希望在一个名为init.el的文件中声明所有我想要在emacs中使用的包。我想知道是否可能在启动emacs时使用例如MELPA加载缺失的包,而无需浏览列表并标记我想要安装的包?

6个回答

42

新回答:

虽然我的原始回答仍然有效,但我现在使用Jordon建议的方法use-package是整洁的Emacs配置的绝佳工具。事实上,当我写下我的原始答案时,我已经在使用它来进行清洁包加载。

那时,我没有仔细阅读README底部, 所以没有意识到它还可以处理软件包安装:

For package.el users

You can use use-package to load packages from ELPA with package.el. This is particularly useful if you share your .emacs between several machines; the relevant packages will download automatically once placed in your .emacs. The :ensure key will install the package automatically if it is not already present:

(use-package tex-site
  :ensure auctex)
在加载了package.el并初始化了我的包仓库之后,我的配置包含:
(if (not (package-installed-p 'use-package))
    (progn
      (package-refresh-contents)
      (package-install 'use-package)))

(require 'use-package)

并随后出现许多类似这样的片段:

;;; Expand-region
(use-package expand-region
  :ensure expand-region
  :bind ("C-=" . er/expand-region))

如果您发现这些信息有用,请给Jordon点赞。

原始回答:

请查看CaskPallet,它们可以一起解决此问题。

来自Cask网站:

Cask for Emacs就像Bundler对于Ruby一样。它旨在使Emacs中的ELPA依赖项管理无痛(尽可能无痛)。这包括您的本地Emacs安装和Emacs软件包开发。

安装Cask之后,您需要将类似以下内容添加到Emacs配置文件中:

(require 'cask)
(cask-initialize)

Cask为elisp依赖项定义了一种特定领域语言。要安装软件包,您需要类似于此处概述的东西:like what is outlined here
(source melpa)

(depends-on "auto-complete")
(depends-on "dash")
(depends-on "f")
(depends-on "flycheck")
(depends-on "helm")
(depends-on "magit")
(depends-on "popup")
(depends-on "projectile")
(depends-on "s")
(depends-on "smartparens")
(depends-on "yasnippet")

请注意,这不是写入您的Emacs配置文件,而是写入~/.emacs.d/Cask
Pallet通过交互安装的软件包(例如通过M-x package-list-packages)更新Cask文件,因此您无需手动维护上述文件。

似乎Cask + Pallet尚未在Windows平台上得到支持,而@work我必须使用它,因此我无法使用这个解决方案。 - taharqa
@Chris 你如何处理软件包依赖关系? - Martinsos
1
@Martinsos,底层的package.el系统处理依赖关系。例如,在MELPA稳定版中查看cider。安装cider时,也会安装任何缺少的依赖项。 - Chris
@Chris谢谢你的回答!那么https://github.com/edvorg/req-package呢?如果依赖项自动解决,那这个有什么好处呢? - Martinsos
1
@Martinsos,我之前从未见过req-package。除了一些微小的语法糖之外,我不认为它比带有:ensureuse-package更有优势。但是,也许我漏看了什么......el-get部分还算有趣。 - Chris
@Chris 谢谢,我会在 req-package 仓库上进一步了解它。也许在 use-package 还没有 :ensure 的时候,它更有用。 - Martinsos

34
所有这些答案都可行,但我强烈建议使用use-package
可以在这里找到:https://github.com/jwiegley/use-package use-package不仅会自动安装缺失的软件包,而且会大大简化你的init.el文件。
以下是我的init.el文件中的一个示例。
;; initial package setup
(push "path/to/use-package" load-path)
(require 'use-package)
(require 'package)
(mapc (lambda(p) (push p package-archives))
      '(("marmalade" . "http://marmalade-repo.org/packages/")
        ("melpa" . "http://melpa.milkbox.net/packages/")))
(package-refresh-contents)
(package-initialize)

;; this will install undo-tree if it's not there
;; and it will set it up how I want
(use-package undo-tree
  :init (global-undo-tree-mode 1)
  :bind (("C-c j" . undo-tree-undo)
         ("C-c k" . undo-tree-redo)
         ("C-c l" . undo-tree-switch-branch)
         ("C-c ;" . undo-tree-visualize))
  :ensure t)

看一下我的init.el文件:https://github.com/jordonbiondo/.emacs.d/blob/master/init.el 每个use-package代码块都会在没有安装指定的包时进行安装,并且它封装了所有其他自定义设置,如键绑定、钩子和其他自定义操作。

每次我启动emacs时,(package-refresh-contents)函数都需要'永远'的时间,即使我的所有库已经下载完毕。我们不能告诉use-package只在需要时才执行它吗? - taharqa
“按需”是指“仅在需要升级软件包时”,但这种方法并不可行,因为除非运行package-refresh-contents,否则您无法知道是否可以升级某个软件包。您可以扫描软件包存档,并仅在存档<某个数字>天或其他条件下运行package-refresh-contents。如果所有软件都已按照您的喜好安装,则可以将package-refresh-contents注释掉。 - Jordon Biondo

7
您的init文件中的一小段代码可以轻松实现这一点:
(setq my-onlinep nil)
(unless
    (condition-case nil
        (delete-process
         (make-network-process
          :name "my-check-internet"
          :host "elpa.gnu.org"
          :service 80))
      (error t))
  (setq my-onlinep t))

(setq my-packages
      '(ack-and-a-half
        ac-nrepl
        ... more packages here ...
        web-mode
        yaml-mode
        yari
        yasnippet))

(when my-onlinep
  (package-refresh-contents)
  (cl-loop for p in my-packages
           unless (package-installed-p p)
           do (package-install p)))

在线检查并不是必需的,但有助于避免在没有互联网连接时卡住Emacs启动。要查看我的完整配置,请访问这里:http://steckerhalter.co.vu/steckemacs.html#sec-2-5

看起来那个URL已经挂了,你能否重定向一下? - Jason Hemann

5
除了上面提到的优秀选项外,您可能还需要查看el-get。我在我的emacs设置中使用它,并且非常满意。
它有什么不同之处?
el-get有两个主要优势:
1)它可以从各种来源获取软件包,该网站列出了以下可能的来源:github、emacswiki、GNU ELPA或Marmalade、私人托管页面、git、bzr、CVS等。
2)如果需要,在软件包安装期间可以用它来运行操作系统命令,例如make。这使得安装具有非elisp部分的软件包更加容易,例如emacs-jedi、ropemacs等。
安装el-get
安装非常简单,您需要将其网站上提供的引导代码添加到您的init文件中。
(add-to-list 'load-path "~/.emacs.d/el-get/el-get")

(unless (require 'el-get nil 'noerror)
  (with-current-buffer
      (url-retrieve-synchronously
       "https://raw.github.com/dimitri/el-get/master/el-get-install.el")
    (goto-char (point-max))
    (eval-print-last-sexp)))

(el-get 'sync)

如果未安装el-get,则会安装它。

安装软件包

然后您可以声明要安装的软件包,并让el-get来安装它们。

;; List of packages you want to install
(defvar my-packages '(auto-complete flycheck smart-parens...))
;; This will install any package from my-packages which is not already installed
(el-get 'sync my-packages)

或者

您可以通过执行el-get-install <package-name> RET来手动安装软件包,但我怀疑您不会想以这种方式安装数十个不同的软件包。

更新软件包

更新软件包只需执行el-get-update以更新特定软件包,或执行el-get-update-all以更新所有已安装的软件包即可。


您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - nicolas
@nicolas 我非常不同意你的观点,以至于我写了一个Emacs的另一个软件包管理器。它附带了一个列出package.el糟糕之处的理由列表! - Radon Rosborough
还有大约10个其他软件包管理器的原因列表。我并不是说没有更好的解决方案。显然,nix-style/functional dependencies非常棒。只是如果这些半吊子的解决方案能够迅速消失,那将是一种改进。 - nicolas
对于每1000个鼓励不道德的、未指定的、半成品的松散代码依赖,我认为有一个警告去坚持2/3/4/..严肃的方法,选择可靠的设计和完整的实现是合理的。 - nicolas

2

0

我喜欢@steckerhalter的答案,因为它不需要安装任何软件包。在此基础上,我进行了以下改进:

  • 查看package-selected-packages变量,以便无需手动指定软件包。当您通过软件包管理器安装软件包时,此变量将由custom-set-variables填充。

  • 仅在存在未安装的软件包时才进行网络检查。

    (let ((not-installed-packages 
           (seq-remove 'package-installed-p package-selected-packages)))
    
      (when (consp not-installed-packages)
    
        (setq my-onlinep nil)
        (unless
            (condition-case nil
                (delete-process
                 (make-network-process
                  :name "my-check-internet"
                  :host "elpa.gnu.org"
                  :service 80))
              (error t))
          (setq my-onlinep t))
    
        (when my-onlinep
          (package-refresh-contents)
          (dolist (p not-installed-packages)
            (package-install p)))
    
        (let ((still-not-installed-packages 
               (seq-remove 'package-installed-p package-selected-packages)))
          (if (consp still-not-installed-packages)
              (message "仍未安装: %s" still-not-installed-packages)
            (message "成功安装: %s" not-installed-packages)))
        ))
    

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