Làm thế nào để tự động cài đặt các gói Emacs bằng cách chỉ định danh sách tên gói?


123

Tôi đang sử dụng packageđể quản lý các tiện ích mở rộng Emacs của mình. Để đồng bộ hóa cài đặt Emacs của tôi trên các máy tính khác nhau, tôi muốn một cách để chỉ định danh sách tên gói trong .emacstệp và sau đó packagecó thể tự động tìm kiếm và cài đặt các gói, do đó tôi không cần phải cài đặt chúng theo cách thủ công bằng cách gọi M-x package-list-packages. Làm thế nào để làm điều đó?


6
Nếu bạn đang dựa vào trình quản lý gói để cài đặt cấu hình của mình, bạn có thể sẽ muốn chỉ định các phiên bản chính xác (và nếu điều đó không thể, hãy tự mình xem xét lưu trữ mọi thứ trong kiểm soát phiên bản), vì nếu không, bạn sẽ không được bảo vệ khi các thư viện được cập nhật và khởi động xung đột.
phils

Câu trả lời:


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
Tôi thích: (hoặc (tệp-tồn tại-p gói-người dùng-dir) (gói-làm mới-nội dung)) từ câu trả lời được chấp nhận. Làm mới gói ở đây làm tăng thời gian khởi động trên các hệ thống đã cài đặt gói. Tuy nhiên, phần còn lại của câu trả lời này là hoàn hảo.
rfinz

Giá trị của biểu tượng dưới dạng biến là void: package-archive-content. Có cách nào mà tôi có thể tạo danh sách trong .emacs và sử dụng một hàm được định nghĩa trong đó để cài đặt tất cả các gói trong danh sách (bỏ qua nếu đã cài đặt, cập nhật nếu cũ) như Vundle cho Vim. Bởi vì tôi không muốn đẩy tất cả các gói trong elpa / sang github, tôi phải làm điều đó mỗi khi một gói được cập nhật trong package.
CodyChan

Ý bạn là gì @rfinz? Có vẻ như package-refresh-contentssẽ chỉ được chạy nếu gói chưa được cài đặt? Làm thế nào là (or (file-exists-p package-user-dir))tốt hơn / làm thế nào nó thậm chí kiểm tra nếu các gói được cài đặt?
Startec

@Startec vâng bạn nói đúng! Nó kiểm tra xem liệu thư mục gói của người dùng có tồn tại hay không và nếu nó không có thì nó chạy package-refresh-contents. Điều này có thể sẽ chỉ được chạy lần đầu tiên bạn mở emac trên một máy tính mới và tôi ổn với điều đó. Nếu một gói cần cập nhật có thể được thực hiện thủ công.
rfinz

2
Nếu bạn đã sử dụng use-package, bạn có thể sử dụng :ensuretừ khóa để cài đặt các gói tự động. Điều này cũng thiết lập package-selected-packagesnếu bạn cần truy cập danh sách gói thông qua tùy chỉnh hoặc theo chương trình.
Nick McCurdy

45

Dựa trên nhận xét của Profpatsch và câu trả lời dưới đây:

(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
Đó có phải là… một bản đồ với các hiệu ứng phụ? Và lạm dụng sự lười biếng của or? Tuyệt vời.
Profpatsch

1
Vâng, mapclà cho các tác dụng phụ. Nhưng tại sao không sử dụng unless?
Profpatsch

Trước đây tôi đã sử dụng mã này và đôi khi nó không hoạt động vì một số lý do không xác định nói rằng "Gói blah-blah không có sẵn để cài đặt" (ở đây blah-blah luôn là phần tử đầu tiên của danh sách). Nếu tôi cài đặt gói đầu tiên theo cách thủ công, mọi thứ hoạt động tốt, nhưng nó không phải là giải pháp. Dù sao, câu trả lời từ Nicolas Dudebois hoạt động tốt.
avp

Tôi cần thiết (package-initialize)trước khi tham chiếu đếnpackage-user-dir
Frank Henard

3
Vậy chúng ta thực sự liệt kê các gói chúng ta muốn cài đặt ở đâu?
Andriy Drozdyuk

41

Emacs 25.1+ sẽ tự động theo dõi các gói do người dùng cài đặt trong biến có thể tùy chỉnh package-selected-packages. package-installsẽ cập nhật biến tùy chỉnh và bạn có thể cài đặt tất cả các gói đã chọn với package-install-selected-packageshàm.

Một ưu điểm thuận tiện khác của phương pháp này là bạn có thể sử dụng package-autoremoveđể tự động loại bỏ các gói không được bao gồm trong package-selected-packages(mặc dù nó sẽ bảo toàn các phần phụ thuộc).

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

Nguồn: http://endlessparentheses.com/new-in-package-el-in-emacs-25-1-user-selected-packages.html


17

Đây là mã tôi sử dụng cho 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)

Nếu bạn không sử dụng MELPA bạn không cần phải yêu cầu nó (và nếu bạn làm melpa.elđã có được trên của bạn load-path(hoặc cài đặt qua MELPA). Các db gói không được làm mới mỗi lần (vì điều này sẽ làm chậm khởi động đáng kể ) - chỉ khi có các gói đã gỡ cài đặt.


Dựa trên câu trả lời của bạn, tôi đã sửa đổi nó một chút và loại bỏ việc sử dụng 'loop' github.com/slipset/emacs/blob/master/ensure-packages.el
slipset

Vâng, ví dụ này thực sự phức tạp hơn mức cần thiết. Đoạn mã tôi hiện đang sử dụng trong Prelude đơn giản hơn nhiều.
Bozhidar Batsov

7

Chưa ai nhắc đến Cask , nhưng nó khá phù hợp cho nhiệm vụ này.

Về cơ bản, bạn tạo ~/.emacs.d/Caskdanh sách các gói bạn muốn cài đặt. Ví dụ:

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

Chạy casktừ dòng lệnh sẽ cài đặt các gói này cho bạn và bất kỳ phụ thuộc nào mà chúng cần.

Ngoài ra, bạn có thể tự động cập nhật các gói đã cài đặt bằng cách sử dụng cask update.


Tôi đã sử dụng thùng trong tệp dotfiles của mình một thời gian rồi, hoạt động rất tốt.
Alastair

Điều đáng tiếc là Cask dường như yêu cầu Python. Tôi tự hỏi liệu có một giải pháp thay thế chỉ elisp không? (Đó là trong một gói; rõ ràng các câu trả lời trên trang này đáp ứng yêu cầu của elisp.)
Peter Jaric

1
Tập lệnh python là một trình bao bọc mỏng xung quanh cask-cli.el, bạn có thể gọi trực tiếp nếu muốn:/path/to/emacs -Q --script /path/to/cask/cask-cli.el -- [args]
Alastair

Hấp dẫn! Không thể sử dụng nó từ bên trong Emacs? Tôi đoán đó là vì nó cũng là một công cụ dành cho nhà phát triển, nhưng hơi bất thường khi phải bước ra ngoài Emacs để đến một CLI để quản lý Emacs.
Peter Jaric

4

Gọi package-installvới tên gói như một ký hiệu. Bạn có thể tìm tên gói cho các gói của mình bằng cách gọi package-installtương tác và điền vào tên. Chức năng package-installed-psẽ cho bạn biết nếu nó đã được cài đặt.

Ví dụ:

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

1
Cảm ơn, nhưng tôi gặp lỗi error: Package dored + 'không có sẵn để cài đặt`. dired + là một gói tôi đã thử với mã của bạn.
RNA

dired+hiển thị khi bạn chạy package-list-packageskhông? Tôi tin rằng bạn sẽ cần thêm mứt cam hoặc melpa vào package-archives. Nếu vậy, bạn có thể chạy (package-install 'dired+)?
ataylor

Trong trường hợp đó, (package-installed-p 'dired+)nên quay lại tvà nó sẽ được bỏ qua trong đoạn mã trên.
ataylor

Một package-installed-pmình hoạt động tốt, nhưng toàn bộ khối mã thì không. Tôi đã thử nhiều gói.
RNA

2
Có vẻ như khúc dạo đầu trong câu trả lời của Nicolas Dudebout sẽ giải quyết được điều đó.
ataylor

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

Tôi muốn kiểm tra xem người dùng có muốn cài đặt các gói trước như được thực hiện trong câu trả lời này không . Ngoài ra, tôi đang làm mới nội dung gói của mình một lần trước khi cài đặt bất kỳ thứ gì. Tôi không chắc liệu đây có phải là cách tốt nhất hay không, nhưng tôi không nghĩ rằng những câu trả lời hàng đầu đã phù hợp với tôi.

(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

Tôi gặp phải một vấn đề mà không có gì xảy ra sau khi thêm (package-install 'org)vào .emacs. Tôi muốn cài đặt phiên bản cập nhật org-modevà cài sẵn org-modenày khá cũ.

Tôi đã đào ra mã nguồn của package-installEmacs 25.3.1. Chức năng tự kiểm tra xem một gói đã được cài đặt hay chưa và từ chối cài đặt nó nếu gói đó đã được cài đặt. Vì vậy, kiểm tra (unless (package-installed-p package) ...)từ câu trả lời 10093312 trên thực tế là không được gọi.

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

Tích hợp sẵn org-modecũng được tính là đã cài đặt và package-installtừ chối cài đặt phiên bản mới hơn từ ELPA. Sau một thời gian đọc package.el, tôi đã nghĩ ra giải pháp sau.

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

Lý do tại sao nó hoạt động là các package-*hàm gia đình xử lý các đối số khác nhau dựa trên việc liệu nó là một biểu tượng hay một package-descđối tượng. Bạn chỉ có thể chỉ định thông tin phiên bản cho package-installthông qua một package-descđối tượng.


0

Đây là của tôi, nó ngắn hơn :)

(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

Đây là một cách khác.

;; 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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.