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

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

44249 次浏览

使用包名作为符号调用 package-install。您可以通过交互式调用 package-install并完成包名称来找到您的包的包名称。函数 package-installed-p将告诉您它是否已经安装。

例如:

(mapc
(lambda (package)
(or (package-installed-p package)
(package-install package)))
'(package1 package2 package3))
; 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)))

根据 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)

下面是我用于 Emacs 序曲的代码:

(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 (因为这会显著降低启动速度)——只有在存在未安装的包时才刷新。

还没有人提到 酒桶,但它非常适合这个任务。

基本上你创建的 ~/.emacs.d/Cask列出了你想要安装的软件包。例如:

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

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

此外,还可以使用 cask update自动更新已安装的包。

我喜欢检查用户是否希望像在 这个答案中那样首先安装软件包。此外,我刷新我的包内容一次,然后再安装任何东西。我不确定这是不是最好的方法,但我不认为最好的答案对我有用。

(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)))
(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)

Emacs 25.1 + 将在可定制的 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

这是我的,比较短

(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))

我遇到了一个问题,什么也没有发生后,加入 (package-install 'org).emacs。我想安装的最新版本的 org-mode和内置的 org-mode是相当老。

我从 Emacs 25.3.1中找到了 package-install的源代码。该函数本身已经检查包是否安装,并拒绝安装它,如果包已经安装。因此,从答案 10093312中检查 (unless (package-installed-p package) ...)实际上是不必要的。

(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-desc对象为 package-install指定版本信息。

还有一个办法。

;; 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)

接近尼古拉斯的 回答:

如果软件包 一号包裹2号包裹3号包裹在本地不存在,则安装它们。如果它们存在,Emacs 将立即加载。

(setq package-archives                                      ;
'(("gnu" . "https://elpa.gnu.org/packages/")          ; declare repositories
("melpa" . "https://melpa.org/packages/")))         ;


(require 'package)                                          ; activate packages
(package-initialize)                                        ; initialize package facility


(setq my-packages
'(package-1
package-2
package-3))


(unless package-archive-contents                            ; unless packages are not available locally, dont refresh package archives
(package-refresh-contents))                               ; refreshing package contents is time-consuming and should be done on demand


(dolist (pkg my-packages)                                   ;
(unless (package-installed-p pkg)                         ; iterate over packages and install missing ones
(package-install pkg)))                                 ;


;; other config below