Comment installer automatiquement les packages Emacs en spécifiant une liste de noms de packages?

123

J'utilise packagepour gérer mes extensions Emacs. Afin de synchroniser mes paramètres Emacs sur différents ordinateurs, j'aimerais avoir un moyen de spécifier une liste de noms de packages dans le .emacsfichier, puis packagede rechercher et d'installer automatiquement les packages, de sorte que je n'ai pas besoin de les installer manuellement en appelant M-x package-list-packages. Comment faire ça?

ARN
la source
6
Si vous comptez sur le gestionnaire de packages pour installer votre configuration, vous voudrez probablement spécifier les versions exactes (et si ce n'est pas possible, envisagez de tout stocker dans le contrôle de version vous-même), sinon vous n'êtes pas protégé lorsque les bibliothèques sont mises à jour et démarrées au conflit.
phils

Réponses:

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
la source
7
Je préfère: (ou (fichier-existe-p package-user-dir) (package-refresh-contents)) de la réponse acceptée. L'actualisation du package ici augmente le temps de démarrage sur les systèmes sur lesquels les packages sont déjà installés. Le reste de cette réponse est cependant parfait.
rfinz
La valeur du symbole en tant que variable est void: package-archive-contents. Existe-t-il un moyen de créer une liste dans .emacs et d'utiliser une fonction qui y est définie pour installer tous les packages de la liste (ignorer si installé, mettre à jour si ancien) comme Vundle pour Vim. Parce que je ne veux pas pousser tous les paquets dans elpa / vers github, je dois le faire chaque fois qu'un paquet est mis à jour dans package.
CodyChan
Que voulez-vous dire @rfinz? Il semble package-refresh-contentsqu'il ne serait exécuté que si le package n'est pas installé? Comment est-il (or (file-exists-p package-user-dir))meilleur / comment vérifie-t-il même si les packages sont installés?
Startec
@Startec oui vous avez raison! Il vérifie si le répertoire de packages de l'utilisateur existe et si ce n'est pas le cas, il s'exécute package-refresh-contents. Cela ne sera probablement exécuté que la première fois que vous ouvrirez emacs sur un nouvel ordinateur, et cela me convient. Si un package doit être mis à jour, cela peut être fait manuellement.
rfinz le
2
Si vous utilisez déjà use-package, vous pouvez utiliser le :ensuremot - clé pour installer automatiquement les packages. Cela configure également package-selected-packagessi vous devez accéder à la liste des packages via la personnalisation ou par programme.
Nick McCurdy
45

Basé sur les commentaires de Profpatsch et les réponses ci-dessous:

(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
la source
2
Est-ce… une carte avec des effets secondaires? Et abuser de la paresse de or? Oh wow.
Profpatsch
1
Eh bien, mapcc'est pour les effets secondaires. Mais pourquoi ne pas l'utiliser unless?
Profpatsch
Auparavant, j'utilisais ce code et parfois cela ne fonctionnait pas pour une raison inconnue en disant "Le paquet blah-blah n'est pas disponible pour l'installation" (ici blah-blah est toujours le premier élément de la liste). Si j'installe le premier package manuellement, tout fonctionne bien, mais ce n'est pas une solution. Quoi qu'il en soit, la réponse de Nicolas Dudebois fonctionne très bien.
avp
J'avais besoin (package-initialize)avant la référence àpackage-user-dir
Frank Henard
3
Alors, où listons-nous réellement les packages que nous voulons installer?
Andriy Drozdyuk
41

Emacs 25.1+ gardera automatiquement une trace des paquets installés par l'utilisateur dans la package-selected-packagesvariable personnalisable . package-installmettra à jour la variable de personnalisation et vous pourrez installer tous les packages sélectionnés avec la package-install-selected-packagesfonction.

Un autre avantage pratique de cette approche est que vous pouvez l'utiliser package-autoremovepour supprimer automatiquement les packages qui ne sont pas inclus dans package-selected-packages(bien que cela préservera les dépendances).

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

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

Nick McCurdy
la source
17

Voici le code que j'utilise pour 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 vous n'utilisez pas MELPA, vous n'avez pas besoin d'en avoir besoin (et si vous devez melpa.elêtre sur votre load-path(ou installé via MELPA). Le package db n'est pas actualisé à chaque fois (car cela ralentirait considérablement le démarrage ) - uniquement là où des packages désinstallés sont présents.

Bozhidar Batsov
la source
Sur la base de votre réponse, je l'ai un peu modifié et supprimé l'utilisation de 'loop' github.com/slipset/emacs/blob/master/ensure-packages.el
slipset
Ouais, cet exemple est vraiment plus complexe qu'il ne devrait l'être. Le code que j'utilise actuellement dans Prelude est beaucoup plus simple.
Bozhidar Batsov
7

Personne n'a encore mentionné Cask , mais il convient tout à fait à cette tâche.

Fondamentalement, vous créez la ~/.emacs.d/Caskliste des packages que vous souhaitez installer. Par exemple:

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

L'exécution à caskpartir de la ligne de commande installera ces packages pour vous, ainsi que toutes les dépendances dont ils ont besoin.

En outre, vous pouvez mettre à jour automatiquement les packages installés à l'aide de cask update.

Alastair
la source
J'utilise cask dans mes fichiers dotfiles depuis un certain temps maintenant, cela fonctionne très bien.
Alastair
Dommage que Cask semble exiger Python. Je me demande s'il existe une alternative élisp uniquement? (C'est dans un paquet; évidemment, les réponses sur cette page répondent à l'exigence d'élisp.)
Peter Jaric
1
Le script python est un fin wrapper autour de cask-cli.el, que vous pouvez appeler directement si vous le souhaitez:/path/to/emacs -Q --script /path/to/cask/cask-cli.el -- [args]
Alastair
Intéressant! N'est-il pas possible de l'utiliser depuis l'intérieur d'Emacs? Je suppose que c'est parce que c'est aussi un outil de développement, mais c'est un peu inhabituel de devoir sortir d'Emacs pour accéder à une CLI pour gérer Emacs.
Peter Jaric
4

Appelez package-installavec le nom du package comme symbole. Vous pouvez trouver les noms de packages de vos packages en appelant de manière package-installinteractive et en complétant le nom. La fonction package-installed-pvous indiquera si elle a déjà été installée.

Par exemple:

(mapc
 (lambda (package)
   (or (package-installed-p package)
       (package-install package)))
 '(package1 package2 package3))
ataylor
la source
1
Merci, mais j'ai une erreur error: Package dired + 'n'est pas disponible pour l'installation'. dired + est un package que j'ai essayé avec votre code.
RNA
Apparaît dired+- il lorsque vous courez package-list-packages? Je crois que vous devrez ajouter de la marmelade ou du melpa à votre package-archives. Si oui, pouvez-vous courir (package-install 'dired+)?
ataylor
Dans ce cas, (package-installed-p 'dired+)devrait revenir tet il sera ignoré dans le code ci-dessus.
ataylor
Le package-installed-pseul fonctionne bien, mais tout le bloc de code ne fonctionne pas. J'ai essayé plusieurs packages.
RNA
2
On dirait que le prélude de la réponse de Nicolas Dudebout résoudra cela.
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
la source
3

J'aime vérifier si l'utilisateur souhaite installer les packages en premier, comme indiqué dans cette réponse . De plus, je rafraîchis le contenu de mon paquet une fois avant d'installer quoi que ce soit. Je ne suis pas sûr que ce soit la meilleure façon, mais je ne pense pas que les principales réponses le faisaient pour moi.

(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
la source
1

Je suis tombé sur un problème qui ne se passait rien après avoir ajouté (package-install 'org)dans .emacs. Je voulais installer la version à jour de org-modeet le intégré org-modeest assez ancien.

J'ai extrait le code source d' package-installEmacs 25.3.1. La fonction auto vérifie déjà si un package est installé ou non et refuse de l'installer si le package est déjà installé. Donc, la vérification (unless (package-installed-p package) ...)de la réponse 10093312 est en fait déplacée.

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

L'intégré org-modecompte également comme installé et package-installrefuse d'installer la version la plus récente d'ELPA. Après avoir passé du temps à lire package.el, j'ai trouvé la solution suivante.

(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 raison pour laquelle cela fonctionne est que package-*les fonctions de famille traitent les arguments différemment selon qu'il s'agit d'un symbole ou d'un package-descobjet. Vous ne pouvez spécifier les informations de version que package-installvia un package-descobjet.

Lei Zhao
la source
0

Voici le mien, c'est plus court :)

(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
la source
0

Voici une autre manière.

;; 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
la source