如何通过指定软件包名称列表自动安装Emacs软件包?


123

package用来管理我的Emacs扩展。为了在不同的计算机上同步我的Emacs设置,我想要一种在.emacs文件中指定软件包名称列表的方法,然后package可以自动搜索并安装软件包,这样就无需通过调用手动安装它们M-x package-list-packages。怎么做?


6
如果您依靠程序包管理器来安装配置,则可能需要指定确切的版本(如果不可能,请考虑自己将所有内容存储在版本控制中),否则在更新和启动库时您将不受保护。冲突。
phils 2012年

Answers:


107
; list the packages you want
(setq package-list '(package1 package2))

; list the repositories containing them
(setq package-archives '(("elpa" . "http://tromey.com/elpa/")
                         ("gnu" . "http://elpa.gnu.org/packages/")
                         ("marmalade" . "http://marmalade-repo.org/packages/")))

; activate all the packages (in particular autoloads)
(package-initialize)

; fetch the list of packages available 
(unless package-archive-contents
  (package-refresh-contents))

; install the missing packages
(dolist (package package-list)
  (unless (package-installed-p package)
    (package-install package)))

7
我更喜欢:(或(file-exists-p package-user-dir)(package-refresh-contents))。此处刷新软件包会增加在已安装软件包的系统上的启动时间。不过,此答案的其余部分是完美的。
rfinz 2014年

Symbol的变量值无效:package-archive-contents。我有什么办法可以在.emacs中创建列表并使用其中定义的功能来安装列表中的所有软件包(如果已安装,则跳过;如果旧,则更新),例如Vundle for Vim。因为我不想将elpa /中的所有软件包都推送到github,所以每次在中更新软件包时都必须这样做package
CodyChan 2014年

@rfinz是什么意思?看起来package-refresh-contents只有在未安装软件包的情况下才能运行?效果如何(or (file-exists-p package-user-dir))/如何检查软件包是否已安装?
Startec '16

@Startec是的,您是对的!它检查用户的package目录是否存在,如果不存在,则运行package-refresh-contents。这可能只会在您第一次在新计算机上打开emacs时运行,我对此表示满意。如果软件包需要更新,可以手动完成。
rfinz

2
如果您已经在使用use-package,则可以使用:ensure关键字自动安装软件包。package-selected-packages如果您需要通过自定义或编程方式访问软件包列表,也会进行设置。
Nick McCurdy

45

根据Profpatsch的评论和以下答案:

(defun ensure-package-installed (&rest packages)
  "Assure every package is installed, ask for installation if it’s not.

Return a list of installed packages or nil for every skipped package."
  (mapcar
   (lambda (package)
     ;; (package-installed-p 'evil)
     (if (package-installed-p package)
         nil
       (if (y-or-n-p (format "Package %s is missing. Install it? " package))
           (package-install package)
         package)))
   packages))

;; make sure to have downloaded archive description.
;; Or use package-archive-contents as suggested by Nicolas Dudebout
(or (file-exists-p package-user-dir)
    (package-refresh-contents))

(ensure-package-installed 'iedit 'magit) ;  --> (nil nil) if iedit and magit are already installed

;; activate installed packages
(package-initialize)

2
那是……有副作用的地图吗?并滥用的懒惰or?哦,哇
Profpatsch 2013年

1
好吧,mapc是为了副作用。但是为什么不使用unless呢?
Profpatsch 2013年

以前,我使用此代码,有时由于某些未知原因而无法运行,说“ Package blah-blah无法安装”(此处blah-blah始终是列表的第一个元素)。如果我手动安装第一个软件包,则一切正常,但这不是解决方案。无论如何,Nicolas Dudebois的答案很好。
2014年

(package-initialize)在提到package-user-dir
Frank Henard 2014年

3
那么,我们实际在哪里列出要安装的软件包?
Andriy Drozdyuk,2014年

41

Emacs 25.1+将在customizable package-selected-packages变量中自动跟踪用户安装的软件包。package-install将更新定制变量,您可以使用该package-install-selected-packages功能安装所有选定的软件包。

这种方法的另一个便利优势是,您可以package-autoremove用来自动删除不包含在其中的软件包package-selected-packages(尽管它将保留相关性)。

(package-initialize)
(unless package-archive-contents
  (package-refresh-contents))
(package-install-selected-packages)

资料来源:http : //endlessparentheses.com/new-in-package-el-in-emacs-25-1-user-selected-packages.html


17

这是我用于Emacs Prelude的代码:

(require 'package)
(require 'melpa)
(add-to-list 'package-archives
             '("melpa" . "http://melpa.milkbox.net/packages/") t)
(package-initialize)

(setq url-http-attempt-keepalives nil)

(defvar prelude-packages
  '(ack-and-a-half auctex clojure-mode coffee-mode deft expand-region
                   gist haml-mode haskell-mode helm helm-projectile inf-ruby
                   magit magithub markdown-mode paredit projectile
                   python sass-mode rainbow-mode scss-mode solarized-theme
                   volatile-highlights yaml-mode yari yasnippet zenburn-theme)
  "A list of packages to ensure are installed at launch.")

(defun prelude-packages-installed-p ()
  (loop for p in prelude-packages
        when (not (package-installed-p p)) do (return nil)
        finally (return t)))

(unless (prelude-packages-installed-p)
  ;; check for new packages (package versions)
  (message "%s" "Emacs Prelude is now refreshing its package database...")
  (package-refresh-contents)
  (message "%s" " done.")
  ;; install the missing packages
  (dolist (p prelude-packages)
    (when (not (package-installed-p p))
      (package-install p))))

(provide 'prelude-packages)

如果你不使用MELPA你不必要求它(如果你这样做melpa.el一定是你的load-path(或通过MELPA安装)。包DB是每次不更新(因为这将启动显著放缓)-仅在存在已卸载软件包的地方。


根据您的回答,我已经对其进行了一些修改,并删除了对“ loop”的使用github.com/slipset/emacs/blob/master/ensure-packages.el
slipset 2013年

是的,这个示例确实比需要的复杂。我目前在Prelude中使用的代码要简单得多。
Bozhidar Batsov

7

还没有人提到Cask,但是它非常适合完成这项任务。

基本上,您会~/.emacs.d/Cask列出要安装的软件包。例如:

(source melpa)
(depends-on "expand-region")
(depends-on "goto-last-change")
; ... etc

cask从命令行运行将为您安装这些软件包及其所需的任何依赖项。

另外,您可以使用来自动更新已安装的软件包cask update


我已经在我的dotfile中使用cask了一段时间了,效果很好。
Alastair 2014年

可惜的木桶似乎需要Python。我想知道是否有一个仅精简版的替代方案?(这是打包的;很显然此页面上的答案符合elisp的要求。)
Peter Jaric 2015年

1
python脚本是cask-cli.el的一个瘦包装,如果您愿意,可以直接调用它:/path/to/emacs -Q --script /path/to/cask/cask-cli.el -- [args]
Alastair

有趣!不能从Emacs内部使用它吗?我猜这是因为它也是一个开发工具,但是必须退出Emacs才能使用CLI来管理Emacs,这有点不寻常。
Peter Jaric 2015年

4

package-install以包裹名称为标志进行呼叫。您可以通过package-install交互式调用并完成名称来查找软件包的软件包名称。该功能package-installed-p将通知您是否已安装。

例如:

(mapc
 (lambda (package)
   (or (package-installed-p package)
       (package-install package)))
 '(package1 package2 package3))

1
谢谢,但是我得到了一个错误的error: Package 指示,dird +'无法用于安装`。dired +是我尝试使用您的代码的软件包。
RNA

dired+跑步时是否显示package-list-packages?我相信您需要在您的饼干中添加果酱或蜜饯package-archives。如果是这样,您可以跑步(package-install 'dired+)吗?
ataylor'4

在这种情况下,(package-installed-p 'dired+)应返回t并且将在上面的代码中将其跳过。
ataylor 2012年

package-installed-p单独工作正常,但整个代码段没有。我试过几个包。
RNA

2
看来尼古拉斯·杜德波特(Nicolas Dudebout)的回答中的前奏将解决这一问题。
ataylor 2012年

4
(require 'cl)
(require 'package)

(setq cfg-var:packages '(
       emmet-mode
       ergoemacs-mode
       flycheck
       flycheck-pyflakes
       monokai-theme
       py-autopep8
       py-isort
       rainbow-mode
       yafolding
       yasnippet))

(defun cfg:install-packages ()
    (let ((pkgs (remove-if #'package-installed-p cfg-var:packages)))
        (when pkgs
            (message "%s" "Emacs refresh packages database...")
            (package-refresh-contents)
            (message "%s" " done.")
            (dolist (p cfg-var:packages)
                (package-install p)))))

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

(cfg:install-packages)

3

我喜欢检查用户是否要按照此答案的要求先安装软件包。另外,在安装任何东西之前,我都会刷新一次包装内容。我不确定这是否是最好的方法,但我认为最重要的答案不是为我做的。

(setq required-pkgs '(jedi flycheck cider clojure-mode paredit markdown-mode jsx-mode company))

(require 'cl)

(setq pkgs-to-install
      (let ((uninstalled-pkgs (remove-if 'package-installed-p required-pkgs)))
        (remove-if-not '(lambda (pkg) (y-or-n-p (format "Package %s is missing. Install it? " pkg))) uninstalled-pkgs)))

(when (> (length pkgs-to-install) 0)
  (package-refresh-contents)
  (dolist (pkg pkgs-to-install)
    (package-install pkg)))

1

我遇到一个问题,添加(package-install 'org)进去后什么也没发生.emacs。我想安装的最新版本,org-mode并且内置的版本org-mode很旧。

package-install从Emacs 25.3.1中挖出了源代码。函数self已经检查软件包是否已安装,如果软件包已经安装,则拒绝安装。因此,实际上是不需要(unless (package-installed-p package) ...)答案10093312的检查的。

(defun package-install (pkg &optional dont-select)
  "Install the package PKG.
PKG can be a package-desc or a symbol naming one of the available packages
in an archive in `package-archives'.  Interactively, prompt for its name.

If called interactively or if DONT-SELECT nil, add PKG to
`package-selected-packages'.

If PKG is a package-desc and it is already installed, don't try
to install it but still mark it as selected."
  (interactive
   (progn
     ;; Initialize the package system to get the list of package
     ;; symbols for completion.
     (unless package--initialized
       (package-initialize t))
     (unless package-archive-contents
       (package-refresh-contents))
     (list (intern (completing-read
                    "Install package: "
                    (delq nil
                          (mapcar (lambda (elt)
                                    (unless (package-installed-p (car elt))
                                      (symbol-name (car elt))))
                                  package-archive-contents))
                    nil t))
           nil)))
  (add-hook 'post-command-hook #'package-menu--post-refresh)
  (let ((name (if (package-desc-p pkg)
                  (package-desc-name pkg)
                pkg)))
    (unless (or dont-select (package--user-selected-p name))
      (package--save-selected-packages
       (cons name package-selected-packages)))
    (if-let ((transaction
              (if (package-desc-p pkg)
                  (unless (package-installed-p pkg)
                    (package-compute-transaction (list pkg)
                                                 (package-desc-reqs pkg)))
                (package-compute-transaction () (list (list pkg))))))
        (package-download-transaction transaction)
      (message "`%s' is already installed" name))))

内置org-mode也算作已安装,并package-install拒绝从ELPA安装较新版本。花一些时间阅读package.el之后,我想到了以下解决方案。

(dolist (package (package-compute-transaction
                  () (list (list 'python '(0 25 1))
                           (list 'org '(20171211)))))
  ;; package-download-transaction may be more suitable here and
  ;; I don't have time to check it
  (package-install package))

它起作用的原因是,package-*族函数根据其是符号还是package-desc对象来不同地处理参数。您只能package-install通过package-desc对象指定版本信息。


0

这是我的,它更短:)

(mapc
 (lambda (package)
   (unless (package-installed-p package)
     (progn (message "installing %s" package)
            (package-refresh-contents)
            (package-install package))))
 '(browse-kill-ring flycheck less-css-mode tabbar org auto-complete undo-tree clojure-mode markdown-mode yasnippet paredit paredit-menu php-mode haml-mode rainbow-mode fontawesome))

0

这是另一种方式。

;; assure every package is installed
(defun ensure-package-installed (&rest packages)
  (let ((user-required-packages
         (seq-remove
          (lambda (package) (package-installed-p package))
          packages)))
    (when user-required-packages
      (package-refresh-contents)
      (dolist (package user-required-packages)
        (package-install package)))))

;; list of packages to install
(ensure-package-installed
 'try
 'which-key)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.