Come installare automaticamente i pacchetti Emacs specificando un elenco di nomi di pacchetti?


123

Sto usando packageper gestire le mie estensioni Emacs. Per sincronizzare le mie impostazioni di Emacs su diversi computer, mi piacerebbe un modo per specificare un elenco di nomi di pacchetti nel .emacsfile e quindi packagecercare e installare automaticamente i pacchetti, in modo da non doverli installare manualmente chiamando M-x package-list-packages. Come farlo?


6
Se ti affidi al gestore di pacchetti per installare la tua configurazione, probabilmente vorrai specificare le versioni esatte (e se ciò non è possibile, considera di memorizzare tutto da solo nel controllo della versione), altrimenti non sei protetto quando le librerie vengono aggiornate e si avvia in conflitto.
phils

Risposte:


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
Preferisco: (o (file-exist-p package-user-dir) (package-refresh-contents)) dalla risposta accettata. L'aggiornamento del pacchetto qui aumenta il tempo di avvio sui sistemi che hanno già i pacchetti installati. Il resto di questa risposta è perfetto, però.
rfinz

Il valore del simbolo come variabile è void: package-archive-contents. C'è un modo in cui posso creare un elenco in .emacs e utilizzare una funzione definita in esso per installare tutti i pacchetti nell'elenco (salta se installato, aggiorna se vecchio) come Vundle per Vim. Poiché non voglio inviare tutti i pacchetti in elpa / a GitHub, devo farlo ogni volta che un pacchetto viene aggiornato in package.
CodyChan

Cosa intendi con @rfinz? Sembra che package-refresh-contentsvenga eseguito solo se il pacchetto non è installato? Com'è (or (file-exists-p package-user-dir))meglio / come controlla anche se i pacchetti sono installati?
Startec

@Startec sì hai ragione! Controlla se la directory del pacchetto dell'utente esiste e, in caso contrario, viene eseguita package-refresh-contents. Questo probabilmente verrà eseguito solo la prima volta che apri emacs su un nuovo computer, e io sto bene con quello. Se un pacchetto deve essere aggiornato, è possibile farlo manualmente.
rfinz

2
Se stai già utilizzando use-package, puoi utilizzare la :ensureparola chiave per installare automaticamente i pacchetti. Questo si imposta anche package-selected-packagesse è necessario accedere all'elenco dei pacchetti tramite personalizzazione o programmazione.
Nick McCurdy,

45

Sulla base dei commenti di Profpatsch e delle risposte seguenti:

(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
Quella è ... una mappa con effetti collaterali? E abusando della pigrizia di or? Oh, wow.
Profpatsch

1
Bene, mapcè per gli effetti collaterali. Ma perché non usare unless?
Profpatsch

In precedenza ho usato questo codice e talvolta non ha funzionato per qualche motivo sconosciuto dicendo "Il pacchetto blah-blah non è disponibile per l'installazione" (qui blah-blah è sempre il primo elemento della lista). Se installo manualmente il primo pacchetto, tutto funziona bene, ma non è una soluzione. Comunque, la risposta di Nicolas Dudebois funziona bene.
avp

Avevo bisogno (package-initialize)prima del riferimento apackage-user-dir
Frank Henard il

3
Quindi dove elenchiamo effettivamente i pacchetti che vogliamo installati?
Andriy Drozdyuk

41

Emacs 25.1+ terrà automaticamente traccia dei pacchetti installati dall'utente nella package-selected-packagesvariabile personalizzabile . package-installaggiornerà la variabile di personalizzazione e potrai installare tutti i pacchetti selezionati con la package-install-selected-packagesfunzione.

Un altro vantaggio conveniente di questo approccio è che puoi usare package-autoremoveper rimuovere automaticamente i pacchetti che non sono inclusi in package-selected-packages(anche se preserverà le dipendenze).

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

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


17

Ecco il codice che uso per 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)

Se non stai usando MELPA non è necessario che tu lo richieda (e se lo fai melpa.eldeve essere sul tuo load-path(o installato tramite MELPA). Il pacchetto db non viene aggiornato ogni volta (in quanto ciò rallenterebbe notevolmente l'avvio ) - solo dove sono presenti pacchetti disinstallati.


Sulla base della tua risposta, l'ho modificata un po 'e ho rimosso l'uso di "loop" github.com/slipset/emacs/blob/master/ensure-packages.el
slipset

Sì, questo esempio è davvero più complesso di quanto dovrebbe essere. Il codice che uso attualmente in Prelude è molto più semplice.
Bozhidar Batsov

7

Nessuno ha menzionato Cask ancora , ma è abbastanza adatto per questo compito.

Fondamentalmente crei l' ~/.emacs.d/Caskelenco dei pacchetti che vuoi installare. Per esempio:

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

In esecuzione cask dalla riga di comando installerà questi pacchetti per te e tutte le dipendenze di cui hanno bisogno.

Inoltre, puoi aggiornare automaticamente i pacchetti installati utilizzando cask update.


Uso botte nei miei dotfile da un po 'di tempo, funziona alla grande.
Alastair

Un peccato che Cask sembra richiedere Python. Mi chiedo se esista un'alternativa solo elisp? (Questo è in un pacchetto; ovviamente le risposte in questa pagina soddisfano il requisito elisp.)
Peter Jaric,

1
Lo script python è un sottile involucro attorno a cask-cli.el, che puoi invocare direttamente se lo desideri:/path/to/emacs -Q --script /path/to/cask/cask-cli.el -- [args]
Alastair

Interessante! Non è possibile usarlo dall'interno di Emacs? Immagino sia perché è anche uno strumento di sviluppo, ma è piuttosto insolito dover uscire da Emacs su una CLI per gestire Emacs.
Peter Jaric

4

Chiama package-installcon il nome del pacchetto come simbolo. Puoi trovare i nomi dei pacchetti per i tuoi pacchetti chiamando in modo package-installinterattivo e completando il nome. La funzionepackage-installed-p ti farà sapere se è già stato installato.

Per esempio:

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

1
Grazie, ma ho ricevuto un errore error: Package dired + 'non è disponibile per l'installazione'. dired + è un pacchetto che ho provato con il tuo codice.
RNA

Si fa dired+vedere quando corri package-list-packages? Credo che dovrai aggiungere marmellata o melpa al tuo package-archives. Se è così, puoi correre (package-install 'dired+)?
ataylor

In tal caso, (package-installed-p 'dired+)dovrebbe tornare te verrà saltato nel codice sopra.
ataylor

Il package-installed-psolo funziona bene, ma l'intero blocco di codice no. Ho provato diversi pacchetti.
RNA

2
Sembra che il preludio nella risposta di Nicolas Dudebout lo risolverà.
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

Mi piace controllare se l'utente desidera installare prima i pacchetti come fatto in questa risposta . Inoltre sto aggiornando il contenuto del mio pacchetto una volta prima di installare qualsiasi cosa. Non sono sicuro che questo sia il modo migliore, ma non credo che le risposte migliori lo stessero facendo per me.

(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

Mi sono imbattuto in un problema che non è successo niente dopo l'aggiunta (package-install 'org)in .emacs. Volevo installare la versione aggiornata di org-modee il built-in org-modeè piuttosto vecchio.

Ho estratto il codice sorgente di package-installda Emacs 25.3.1. La funzione self controlla già se un pacchetto è installato o meno e si rifiuta di installarlo se il pacchetto è già installato. Quindi il controllo (unless (package-installed-p package) ...)dalla risposta 10093312 è in realtà non richiesto.

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

Anche il built-in org-modeconta come installato e si package-installrifiuta di installare la versione più recente da ELPA. Dopo aver passato un po 'di tempo a leggere package.el, ho trovato la seguente soluzione.

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

Il motivo per cui funziona è che le package-*funzioni familiari gestiscono gli argomenti in modo diverso a seconda che si tratti di un simbolo o di un package-descoggetto. È possibile specificare solo le informazioni sulla versione package-installtramite un package-descoggetto.


0

Ecco il mio, è più 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))

0

Ecco un altro modo.

;; 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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.