Як автоматично встановити пакети Emacs, вказавши список назв пакетів?


123

Я використовую packageдля управління своїми розширеннями Emacs. Щоб синхронізувати налаштування Emacs на різних комп’ютерах, я хотів би вказати список імен пакунків у .emacsфайлі, а потім packageміг автоматично шукати та встановлювати пакунки, так що мені не потрібно встановлювати їх вручну, зателефонувавши M-x package-list-packages. Як це зробити?


6
Якщо ви покладаєтесь на менеджер пакунків для встановлення своєї конфігурації, ви, ймовірно, захочете вказати точні версії (а якщо це неможливо, розглянути можливість зберігати все в контролі версій самостійно), оскільки в іншому випадку ви не захищені, коли бібліотеки оновлюються та запускаються конфліктувати.
phils

Відповіді:


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
Я вважаю за краще: (або (файл-існує-p пакет-користувач-реж) (пакет-оновлення вмісту)) з прийнятої відповіді. Оновлення пакунків тут збільшує час запуску в системах, на яких уже встановлені пакети. Однак решта цієї відповіді ідеальна.
rfinz

Значення символу як змінної - недійсне: пакет-архів-вміст. Чи є спосіб, що я можу скласти список у .emacs і використовувати функцію, визначену в ньому, для встановлення всіх пакетів у списку (пропустити, якщо встановлено, оновити, якщо старі), як Vundle for Vim. Оскільки я не хочу натискати всі пакунки в elpa / to github, я повинен це робити щоразу, коли пакет оновлений у package.
CodyChan

Що ти маєш на увазі @rfinz? Схоже, package-refresh-contentsце буде запущено, лише якщо пакет не встановлений? Як (or (file-exists-p package-user-dir))краще / як він навіть перевіряє, чи встановлені пакети?
Startec

@Startec так, ти прав! Він перевіряє, чи існує каталог пакунків користувача, і чи він не працює package-refresh-contents. Це, ймовірно, буде запущено лише вперше, коли ви відкриєте emacs на новому комп’ютері, і я з цим добре. Якщо пакет потребує оновлення, це можна зробити вручну.
rfinz

2
Якщо ви вже використовуєте use-package, ви можете використовувати :ensureключове слово для автоматичного встановлення пакетів. Це також встановлюється, package-selected-packagesякщо вам потрібно отримати доступ до списку пакунків через налаштування або програмно.
Нік МакКерді

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

1
Ну, mapcдля побічних ефектів. Але чому б не використовувати unless?
Profpatsch

Раніше я використовував цей код, а іноді він не працював з незрозумілої причини, кажучи: "Blah-blah недоступний для встановлення" (тут бла-бла завжди є першим елементом списку). Якщо я встановлю перший пакет вручну, все працює добре, але це не рішення. У будь-якому випадку, відповідь від Ніколя Дюдеа справляється чудово.
AVP

Мені потрібно було (package-initialize)перед посиланням наpackage-user-dir
Френк Хенард

3
То де ми насправді перераховуємо пакети, які ми хочемо встановити?
Андрій Дроздюк

41

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


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 не оновлюється щоразу (оскільки це значно уповільнить запуск) ) - лише там, де є невстановлені пакети.


Виходячи з вашої відповіді, я трохи змінив її та видалив використання 'петлі' github.com/slipset/emacs/blob/master/ensure-packages.el
слайс

Так, цей приклад насправді складніший, ніж повинен бути. Код, який я зараз використовую в Prelude, набагато простіший.
Божидар Бацов

7

Ще ніхто не згадав про Каска , але він цілком підходить для цього завдання.

В основному ви створюєте ~/.emacs.d/Caskперелік пакунків, які ви хочете встановити. Наприклад:

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

Запустившись caskз командного рядка, встановіть ці пакети для вас та будь-які залежності, які вони потребують.

Крім того, ви можете автоматично оновлювати встановлені пакети за допомогою cask update.


Я вже деякий час використовую cask у своїх dotfiles , чудово працює.
Аластер

Шкода, що Cask, здається, вимагає Python. Цікаво, чи існує альтернатива лише для еліпса? (Це в пакеті; очевидно, що відповіді на цій сторінці відповідають вимозі
Elisp

1
Сценарій python - це тонка обгортка навколо cask-cli.el, яку ви можете викликати безпосередньо, якщо вам подобається:/path/to/emacs -Q --script /path/to/cask/cask-cli.el -- [args]
Alastair

Цікаво! Неможливо використовувати його зсередини Emacs? Я думаю, це тому, що це також інструмент для розробників, але це незвично, що потрібно керувати поза Emacs до CLI для управління Emacs.
Петро Ярич

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 накреслено + "недоступно для встановлення". dired + - це пакет, який я спробував із вашим кодом.
РНК

Чи dired+з’являється при запуску package-list-packages? Я вважаю, що вам потрібно буде додати або мармелад, або мельпу до своїх package-archives. Якщо так, чи можете ви бігти (package-install 'dired+)?
ataylor

У такому випадку (package-installed-p 'dired+)слід повернутися, tі це буде пропущено у наведеному вище коді.
ataylor

package-installed-pУ поодинці працює відмінно, але весь блок коду не робить. Я спробував кілька пакетів.
РНК

2
Схоже, прелюдія у відповіді Ніколя Дудебу вирішить це.
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

Мені подобається перевірити, чи користувач хоче спочатку встановити пакунки, як це зроблено в цій відповіді . Також я оновлюю вміст свого пакету один раз, перш ніж щось встановлювати. Я не впевнений, що це найкращий спосіб, але я не думаю, що найкращі відповіді робили це для мене.

(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-installEmacs 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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.