Wie installiere ich Emacs-Pakete automatisch, indem ich eine Liste von Paketnamen angebe?

123

Ich verwende package, um meine Emacs-Erweiterungen zu verwalten. Um meine Emacs-Einstellungen auf verschiedenen Computern zu synchronisieren, möchte ich eine Liste von Paketnamen in der .emacsDatei angeben und dann packagedie Pakete automatisch suchen und installieren, sodass ich sie nicht manuell durch Aufrufen installieren muss M-x package-list-packages. Wie geht das?

RNA
quelle
6
Wenn Sie sich bei der Installation Ihrer Konfiguration auf den Paketmanager verlassen, möchten Sie wahrscheinlich die genauen Versionen angeben (und wenn dies nicht möglich ist, sollten Sie alles in der Versionskontrolle selbst speichern), da Sie sonst nicht geschützt sind, wenn Bibliotheken aktualisiert und gestartet werden in Konflikt geraten.
Phils

Antworten:

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
quelle
7
Ich bevorzuge: (oder (Datei-existiert-p Paket-Benutzer-Verzeichnis) (Paket-Aktualisierungs-Inhalt)) aus der akzeptierten Antwort. Die Paketaktualisierung erhöht hier die Startzeit auf Systemen, auf denen die Pakete bereits installiert sind. Der Rest dieser Antwort ist jedoch perfekt.
Rfinz
Der Wert des Symbols als Variable ist ungültig: Paket-Archiv-Inhalt. Gibt es eine Möglichkeit, eine Liste in .emacs zu erstellen und eine darin definierte Funktion zu verwenden, um alle Pakete in der Liste (überspringen, falls installiert, aktualisieren, falls alt) wie Vundle for Vim zu installieren. Da ich nicht alle Pakete in elpa / to github pushen möchte, muss ich dies jedes Mal tun, wenn ein Paket in aktualisiert wird package.
CodyChan
Was meinst du mit @rfinz? Es sieht so aus, als package-refresh-contentswürde es nur ausgeführt, wenn das Paket nicht installiert ist? Wie ist es (or (file-exists-p package-user-dir))besser / wie wird überhaupt geprüft, ob Pakete installiert sind?
Startec
@Startec ja du bist richtig! Es wird überprüft, ob das Paketverzeichnis des Benutzers vorhanden ist und ob es nicht ausgeführt wird package-refresh-contents. Dies wird wahrscheinlich nur ausgeführt, wenn Sie Emacs zum ersten Mal auf einem neuen Computer öffnen, und damit bin ich einverstanden. Wenn ein Paket aktualisiert werden muss, kann dies manuell erfolgen.
Rfinz
2
Wenn Sie bereits verwenden use-package, können Sie das :ensureSchlüsselwort verwenden, um Pakete automatisch zu installieren. Dies wird auch eingerichtet, package-selected-packageswenn Sie über Anpassen oder programmgesteuert auf die Paketliste zugreifen müssen.
Nick McCurdy
45

Basierend auf Kommentaren von Profpatsch und Antworten unten:

(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)
RNA
quelle
2
Ist das… eine Karte mit Nebenwirkungen? Und die Faulheit von missbrauchen or? Oh wow.
Profpatsch
1
Nun, mapcist für Nebenwirkungen. Aber warum nicht verwenden unless?
Profpatsch
Früher habe ich diesen Code verwendet und manchmal funktionierte er aus einem unbekannten Grund nicht und sagte "Paket bla-bla ist nicht für die Installation verfügbar" (hier ist bla-bla immer das erste Element der Liste). Wenn ich das erste Paket manuell installiere, funktioniert alles einwandfrei, aber es ist keine Lösung. Wie auch immer, die Antwort von Nicolas Dudebois funktioniert gut.
Avp
Ich brauchte (package-initialize)vor dem Verweis aufpackage-user-dir
Frank Henard
3
Wo listen wir die Pakete auf, die installiert werden sollen?
Andriy Drozdyuk
41

Emacs 25.1+ verfolgt automatisch die vom Benutzer installierten Pakete in der anpassbaren package-selected-packagesVariablen. package-installaktualisiert die Anpassungsvariable und Sie können alle ausgewählten Pakete mit der package-install-selected-packagesFunktion installieren .

Ein weiterer praktischer Vorteil dieses Ansatzes besteht darin, dass Sie package-autoremovePakete, die nicht in enthalten sind, automatisch entfernen können package-selected-packages(obwohl Abhängigkeiten erhalten bleiben).

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

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

Nick McCurdy
quelle
17

Hier ist der Code, den ich für Emacs Prelude verwende :

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

Wenn Sie MELPA nicht verwenden, müssen Sie es nicht benötigen (und wenn Sie dies tun, müssen Sie melpa.eles auf Ihrem load-pathComputer haben (oder über MELPA installiert haben). Das Paket db wird nicht jedes Mal aktualisiert (da dies den Start erheblich verlangsamen würde ) - nur wenn deinstallierte Pakete vorhanden sind.

Bozhidar Batsov
quelle
Basierend auf Ihrer Antwort habe ich es ein wenig geändert und die Verwendung von 'loop' github.com/slipset/emacs/blob/master/ensure-packages.el
Slipset
Ja, dieses Beispiel ist wirklich komplexer als es sein muss. Der Code, den ich derzeit in Prelude verwende, ist viel einfacher.
Bozhidar Batsov
7

Noch hat niemand Cask erwähnt , aber es ist für diese Aufgabe durchaus geeignet.

Grundsätzlich erstellen Sie eine ~/.emacs.d/CaskListe der Pakete, die Sie installieren möchten. Beispielsweise:

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

Wenn Sie casküber die Befehlszeile ausgeführt werden, werden diese Pakete und alle erforderlichen Abhängigkeiten für Sie installiert.

Sie können installierte Pakete auch automatisch mit aktualisieren cask update.

Alastair
quelle
Ich benutze seit einiger Zeit Fass in meinen Punktedateien , funktioniert großartig.
Alastair
Schade, dass Cask Python benötigt. Ich frage mich, ob es eine Alternative nur für Elisp gibt. (Das ist in einem Paket; offensichtlich erfüllen die Antworten auf dieser Seite die Elisp-Anforderung.)
Peter Jaric
1
Das Python-Skript ist ein dünner Wrapper um cask-cli.el, den Sie direkt aufrufen können, wenn Sie möchten:/path/to/emacs -Q --script /path/to/cask/cask-cli.el -- [args]
Alastair
Interessant! Ist es nicht möglich, es innerhalb von Emacs zu verwenden? Ich denke, das liegt daran, dass es auch ein Entwicklungswerkzeug ist, aber es ist etwas ungewöhnlich, außerhalb von Emacs zu einer CLI zu wechseln, um Emacs zu verwalten.
Peter Jaric
4

Rufen Sie package-installmit dem Paketnamen als Symbol auf. Sie können die Paketnamen für Ihre Pakete finden, indem Sie package-installinteraktiv aufrufen und den Namen eingeben. Die Funktion package-installed-pinformiert Sie, wenn sie bereits installiert wurde.

Beispielsweise:

(mapc
 (lambda (package)
   (or (package-installed-p package)
       (package-install package)))
 '(package1 package2 package3))
ataylor
quelle
1
Danke, aber ich habe eine Fehlermeldung error: Package erhalten + 'steht nicht zur Installation zur Verfügung`. dired + ist ein Paket, das ich mit Ihrem Code ausprobiert habe.
RNA
Hat dired+angezeigt, wenn Sie laufen package-list-packages? Ich glaube, Sie müssen entweder Marmelade oder Melpa zu Ihrem hinzufügen package-archives. Wenn ja, kannst du rennen (package-install 'dired+)?
Ataylor
In diesem Fall (package-installed-p 'dired+)sollte zurückgegeben werden tund es wird im obigen Code übersprungen.
Ataylor
Das package-installed-palleine funktioniert gut, aber der ganze Codeblock nicht. Ich habe mehrere Pakete ausprobiert.
RNA
2
Es sieht so aus, als würde der Auftakt in Nicolas Dudebouts Antwort das lösen.
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
quelle
3

Ich überprüfe gerne, ob der Benutzer die Pakete zuerst installieren möchte, wie in dieser Antwort beschrieben . Außerdem aktualisiere ich meinen Paketinhalt einmal, bevor ich etwas installiere. Ich bin mir nicht sicher, ob dies der beste Weg ist, aber ich glaube nicht, dass die besten Antworten dies für mich getan haben.

(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
quelle
1

Ich lief in ein Problem , dass nichts passiert nach dem Hinzufügen (package-install 'org)in .emacs. Ich wollte die aktuelle Version von installieren org-modeund die eingebaute org-modeist ziemlich alt.

Ich habe den Quellcode package-installvon Emacs 25.3.1 ausgegraben. Die Funktion selbst prüft bereits, ob ein Paket installiert ist oder nicht, und lehnt die Installation ab, wenn das Paket bereits installiert ist. Der Scheck (unless (package-installed-p package) ...)aus Antwort 10093312 ist also in der Tat unangebracht.

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

Das integrierte Gerät gilt org-modeauch als installiert und package-installweigert sich, die neuere Version von ELPA zu installieren. Nachdem ich einige Zeit damit verbracht hatte, package.el zu lesen, kam ich auf die folgende Lösung.

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

Der Grund, warum es funktioniert, ist, dass package-*Familienfunktionen die Argumente unterschiedlich behandeln, je nachdem, ob es sich um ein Symbol oder ein package-descObjekt handelt. Sie können Versionsinformationen nur package-installüber ein package-descObjekt angeben .

Lei Zhao
quelle
0

Hier ist meins, es ist kürzer :)

(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
quelle
0

Hier ist ein anderer Weg.

;; 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
quelle