如何通过指定包名列表来自动安装 Emacs 包?
Posted
技术标签:
【中文标题】如何通过指定包名列表来自动安装 Emacs 包?【英文标题】:How to automatically install Emacs packages by specifying a list of package names? 【发布时间】:2012-04-22 22:54:10 【问题描述】:我正在使用package
来管理我的 Emacs 扩展。为了在不同的计算机上同步我的 Emacs 设置,我想在.emacs
文件中指定一个包名列表,然后package
可以自动搜索和安装这些包,这样我就不需要通过调用M-x package-list-packages
手动安装它们。该怎么做?
【问题讨论】:
如果您依赖包管理器来安装您的配置,您可能需要指定确切的版本(如果不可能,请考虑自己将所有内容存储在版本控制中),否则您将当库更新并开始冲突时不受保护。 【参考方案1】:这是另一种方式。
;; 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】:我遇到了一个问题,将(package-install 'org)
添加到.emacs
后没有任何反应。我想安装最新版本的org-mode
,而内置的org-mode
已经很旧了。
我从 Emacs 25.3.1 中挖出了package-install
的源代码。函数 self 已经检查是否安装了包,如果包已经安装,则拒绝安装它。因此,来自答案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
指定版本信息。
【讨论】:
【参考方案3】:这是我的,更短:)
(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))
【讨论】:
【参考方案4】: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
【讨论】:
【参考方案5】:(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)
【讨论】:
【参考方案6】:我喜欢检查用户是否想先安装软件包,就像在this answer 中所做的那样。此外,在安装任何东西之前,我都会刷新一次我的包内容。我不确定这是否是最好的方法,但我认为最佳答案不是为我做的。
(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)))
【讨论】:
【参考方案7】:还没有人提到Cask,但它很适合这个任务。
基本上你创建~/.emacs.d/Cask
列出你想要安装的包。例如:
(source melpa)
(depends-on "expand-region")
(depends-on "goto-last-change")
; ... etc
从命令行运行 cask
将为您安装这些软件包以及它们所需的任何依赖项。
此外,您可以使用cask update
自动更新已安装的软件包。
【讨论】:
我在dotfiles 中使用木桶已经有一段时间了,效果很好。 可惜的是 Cask 似乎需要 Python。我想知道是否有一个 elisp-only 替代方案? (这是在一个包中;显然此页面上的答案符合 elisp 要求。) python 脚本是对 cask-cli.el 的薄包装,如果您愿意,可以直接调用它:/path/to/emacs -Q --script /path/to/cask/cask-cli.el -- [args]
有趣!难道不能从 Emacs 内部使用它吗?我想这是因为它也是一个开发工具,但是不得不跳出 Emacs 到 CLI 来管理 Emacs 有点不寻常。【参考方案8】:
基于 Profpatsch 的 cmets 和以下答案:
(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)
【讨论】:
那是……有副作用的地图吗?并滥用or
的懒惰?哦,哇。
好吧,mapc
是针对副作用的。但是为什么不使用unless
呢?
以前我使用过这段代码,有时它因为一些未知的原因不起作用,说“软件包 blah-blah 不可用于安装”(这里 blah-blah 始终是列表的第一个元素)。如果我手动安装第一个包,一切正常,但这不是解决方案。无论如何,Nicolas Dudebois 的回答很好。
在引用package-user-dir
之前我需要(package-initialize)
那么我们到底在哪里列出我们想要安装的包呢?【参考方案9】:
; 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)))
【讨论】:
我更喜欢:(或 (file-exists-p package-user-dir) (package-refresh-contents)) 从接受的答案。此处的软件包刷新会增加已安装软件包的系统的启动时间。不过,这个答案的其余部分是完美的。 符号作为变量的值是 void:package-archive-contents。有什么方法可以在 .emacs 中创建一个列表并使用其中定义的函数来安装列表中的所有包(如果安装则跳过,如果旧则更新),例如 Vundle for Vim。因为不想把elpa/里的所有包都推到github上,所以每次package
里更新包都得这么做。
@rfinz 是什么意思?看起来package-refresh-contents
只有在未安装软件包的情况下才会运行? (or (file-exists-p package-user-dir))
如何更好/它甚至如何检查是否安装了软件包?
@Startec 是的,你是对的!它检查用户的包目录是否存在,如果不存在则运行package-refresh-contents
。这可能只会在您第一次在新计算机上打开 emacs 时运行,我对此很好。如果包需要更新,可以手动完成。
如果您已经在使用use-package
,您可以使用:ensure
关键字自动安装软件包。如果您需要通过自定义或编程方式访问包列表,这也会设置package-selected-packages
。【参考方案10】:
这是我用于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 不会每次都刷新(因为这会显着减慢启动速度)- 仅在存在已卸载软件包的情况下。
【讨论】:
根据您的回答,我对其进行了一些修改并删除了“循环”github.com/slipset/emacs/blob/master/ensure-packages.el 的使用 是的,这个例子确实比它需要的复杂。我目前在 Prelude 中使用的代码要简单得多。【参考方案11】:使用包名作为符号调用package-install
。您可以通过交互调用package-install
并填写名称来找到您的包的包名称。 package-installed-p
函数会告诉你它是否已经安装。
例如:
(mapc
(lambda (package)
(or (package-installed-p package)
(package-install package)))
'(package1 package2 package3))
【讨论】:
谢谢,但我收到一个错误error: Package
dired+' is not available for installation`。 dired+ 是我用你的代码试过的一个包。
当你运行package-list-packages
时,dired+
会出现吗?我相信您需要将果酱或melpa 添加到您的package-archives
。如果是这样,你能运行(package-install 'dired+)
吗?
在这种情况下,(package-installed-p 'dired+)
应该返回t
,并且会在上面的代码中被跳过。
package-installed-p
单独工作正常,但整个代码块却不行。我已经尝试了几个包。
看起来 Nicolas Dudebout 的回答中的前奏会解决这个问题。以上是关于如何通过指定包名列表来自动安装 Emacs 包?的主要内容,如果未能解决你的问题,请参考以下文章