¿Cómo instalar automáticamente los paquetes de Emacs especificando una lista de nombres de paquetes?

123

Estoy usando packagepara administrar mis extensiones de Emacs. Para sincronizar mi configuración de Emacs en diferentes computadoras, me gustaría una forma de especificar una lista de nombres de paquetes en el .emacsarchivo y luego packagepodría buscar e instalar automáticamente los paquetes, de modo que no necesite instalarlos manualmente llamando M-x package-list-packages. ¿Como hacer eso?

ARN
fuente
66
Si confía en el administrador de paquetes para instalar su configuración, es probable que desee especificar las versiones exactas (y si eso no es posible, considere almacenar todo en el control de versiones usted mismo), de lo contrario no estará protegido cuando las bibliotecas se actualicen y comiencen al conflicto
phils

Respuestas:

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)))
Nicolas Dudebout
fuente
77
Prefiero: (o (file-exist-p package-user-dir) (package-refresh-contents)) de la respuesta aceptada. La actualización del paquete aquí aumenta el tiempo de inicio en los sistemas que ya tienen los paquetes instalados. Sin embargo, el resto de esta respuesta es perfecta.
rfinz
El valor del símbolo como variable es nulo: package-archive-contents. ¿Hay alguna manera de que pueda hacer una lista en .emacs y usar una función definida en él para instalar todos los paquetes en la lista (omitir si está instalado, actualizar si es antiguo) como Vundle for Vim. Como no quiero insertar todos los paquetes en elpa / to github, tengo que hacerlo cada vez que se actualiza un paquete package.
CodyChan
¿Qué quieres decir @rfinz? ¿Parece package-refresh-contentsque solo se ejecutará si el paquete no está instalado? ¿Cómo es (or (file-exists-p package-user-dir))mejor / cómo incluso verifica si los paquetes están instalados?
Startec
@Startec sí, tienes razón! Comprueba si existe el directorio de paquetes del usuario y, si no es así, se ejecuta package-refresh-contents. Esto probablemente solo se ejecutará la primera vez que abra emacs en una computadora nueva, y estoy de acuerdo con eso. Si un paquete necesita una actualización, puede hacerlo manualmente.
rfinz
2
Si ya está usando use-package, puede usar la :ensurepalabra clave para instalar paquetes automáticamente. Esto también se configura package-selected-packagessi necesita acceder a la lista de paquetes a través de personalizar o mediante programación.
Nick McCurdy
45

Basado en los comentarios de Profpatsch y las respuestas a continuación:

(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)
ARN
fuente
2
¿Es eso ... un mapa con efectos secundarios? Y mal uso de la pereza de or? Oh wow.
Profpatsch
1
Bueno, mapces por efectos secundarios. ¿Pero por qué no usar unless?
Profpatsch
Anteriormente utilicé este código y, a veces, no funcionó por alguna razón desconocida que decía "El paquete blah-blah no está disponible para la instalación" (aquí blah-blah es siempre el primer elemento de la lista). Si instalo el primer paquete manualmente, todo funciona bien, pero no es una solución. De todos modos, la respuesta de Nicolas Dudebois funciona bien.
avp
Necesitaba (package-initialize)antes de la referencia apackage-user-dir
Frank Henard
3
Entonces, ¿dónde enumeramos realmente los paquetes que queremos instalar?
Andriy Drozdyuk
41

Emacs 25.1+ realizará un seguimiento automático de los paquetes instalados por el usuario en la package-selected-packagesvariable personalizable . package-installactualizará la variable de personalización, y puede instalar todos los paquetes seleccionados con la package-install-selected-packagesfunción.

Otra ventaja conveniente de este enfoque es que puede usar package-autoremovepara eliminar automáticamente los paquetes que no están incluidos package-selected-packages(aunque preservará las dependencias).

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

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

Nick McCurdy
fuente
17

Aquí está el código que uso para 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)

Si no está utilizando MELPA, no es necesario que lo requiera (y si melpa.eltiene que estar en su load-path(o instalado a través de MELPA). El paquete db no se actualiza cada vez (ya que esto retrasaría significativamente el inicio) ) - solo donde hay paquetes desinstalados presentes.

Bozhidar Batsov
fuente
Basado en su respuesta, lo modifiqué un poco y eliminé
slipset
Sí, este ejemplo es realmente más complejo de lo que debe ser. El código que uso actualmente en Prelude es mucho más simple.
Bozhidar Batsov
7

Nadie ha mencionado Cask todavía, pero es bastante adecuado para esta tarea.

Básicamente, crea una ~/.emacs.d/Casklista de los paquetes que desea instalar. Por ejemplo:

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

Ejecutar caskdesde la línea de comandos instalará estos paquetes por usted y cualquier dependencia que necesiten.

Además, puede actualizar automáticamente los paquetes instalados usando cask update.

Alastair
fuente
He estado usando barril en mis archivos de puntos por algún tiempo, funciona muy bien.
Alastair
Una pena que Cask parezca requerir Python. Me pregunto si hay una alternativa de solo elisp? (Eso está en un paquete; obviamente las respuestas en esta página cumplen con el requisito de elisp.)
Peter Jaric
1
El script de Python es una envoltura delgada alrededor de cask-cli.el, que puede invocar directamente si lo desea:/path/to/emacs -Q --script /path/to/cask/cask-cli.el -- [args]
Alastair
¡Interesante! ¿No es posible usarlo desde el interior de Emacs? Supongo que es porque también es una herramienta de desarrollo, pero es inusual tener que salir de Emacs a una CLI para administrar Emacs.
Peter Jaric
4

Llame package-installcon el nombre del paquete como símbolo. Puede encontrar los nombres de paquete para sus paquetes llamando de forma package-installinteractiva y completando el nombre. La función package-installed-ple informará si ya está instalada.

Por ejemplo:

(mapc
 (lambda (package)
   (or (package-installed-p package)
       (package-install package)))
 '(package1 package2 package3))
ataylor
fuente
1
Gracias, pero recibí un error error: Package dired + 'no está disponible para la instalación'. dired + es un paquete que probé con su código.
ARN
¿ dired+Aparece cuando corres package-list-packages? Creo que necesitarás agregar mermelada o melpa a tu package-archives. Si es así, ¿puedes correr (package-install 'dired+)?
ataylor
En ese caso, (package-installed-p 'dired+)debería regresar ty se omitirá en el código anterior.
ataylor
El package-installed-psolo funciona bien, pero todo el bloque de código no. He probado varios paquetes.
ARN
2
Parece que el preludio de la respuesta de Nicolas Dudebout resolverá eso.
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)
Dunaevsky Maxim
fuente
3

Me gusta comprobar si el usuario quiere instalar los paquetes primero como se hace en esta respuesta . También estoy actualizando el contenido de mi paquete una vez antes de instalar cualquier cosa. No estoy seguro de si esta es la mejor manera, pero no creo que las mejores respuestas lo estuvieran haciendo por mí.

(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)))
Frank Henard
fuente
1

Me encontré con un problema que no pasó nada después de añadir (package-install 'org)en .emacs. Quería instalar la versión actualizada de org-modey el incorporado org-modees bastante antiguo.

package-installExtraje el código fuente de Emacs 25.3.1. La función self ya comprueba si un paquete está instalado o no y se niega a instalarlo si el paquete ya está instalado. Por lo tanto, la comprobación (unless (package-installed-p package) ...)de la respuesta 10093312 de hecho no es necesaria.

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

El incorporado org-modetambién cuenta como instalado y se package-installniega a instalar la versión más nueva de ELPA. Después de pasar algún tiempo leyendo package.el, se me ocurrió la siguiente solución.

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

La razón por la que funciona es que las package-*funciones familiares manejan los argumentos de manera diferente en función de si es un símbolo o un package-descobjeto. Solo puede especificar información de versión a package-installtravés de un package-descobjeto.

Lei Zhao
fuente
0

Aquí está el mío, es más corto :)

(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))
yPhil
fuente
0

Aquí hay otra forma.

;; 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)
Yogesh Kamat
fuente