2012-04-10 18 views
103

Używam package do zarządzania rozszerzeniami Emacs. Aby zsynchronizować moje ustawienia Emacsa na różnych komputerach, chciałbym podać sposób na określenie nazwy pakietu w pliku .emacs, a następnie package może automatycznie wyszukiwać i instalować pakiety, więc nie muszę instalować ich ręcznie przez dzwoniąc pod numer M-x package-list-packages. Jak to zrobić?Jak automatycznie instalować pakiety Emacs, określając listę nazw pakietów?

+5

Jeśli polegasz na menedżerze pakietów, aby zainstalować konfigurację, prawdopodobnie będziesz chciał podać dokładne wersje (a jeśli nie jest to możliwe, rozważ przechowywanie wszystkiego w kontroli wersji samodzielnie), ponieważ w przeciwnym razie nie będziesz chroniony, gdy biblioteki będą zaktualizowany i zaczynają się konflikty. – phils

Odpowiedz

44

Na podstawie uwag Profpatsch i odpowiedzi poniżej:

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

Czy to ... mapa z efektami ubocznymi? I nadużywasz lenistwa "lub"? Och, wow. – Profpatsch

+1

Cóż, 'mapc' służy do efektów ubocznych. Ale dlaczego nie użyć 'chyba że'? – Profpatsch

+0

Wcześniej użyłem tego kodu i czasami nie działało z jakiegoś nieznanego powodu mówiąc "Pakiet bla bla" nie jest dostępny do instalacji "(tutaj bla-bla zawsze jest pierwszym elementem listy). Jeśli ręcznie zainstaluję pierwszy pakiet, wszystko działa poprawnie, ale nie jest to rozwiązanie. W każdym razie odpowiedź Nicolas Dudebois działa dobrze. – avp

4

Zadzwoń pod package-install z nazwą pakietu jako symbolem. Możesz znaleźć nazwy pakietów dla swoich pakietów, dzwoniąc pod numer package-install i kończąc na nazwie. Funkcja package-installed-p poinformuje Cię, czy jest już zainstalowana.

Na przykład:

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

Dzięki, ale dostałem komunikat o błędzie 'error: Package' dired + 'nie jest dostępny do instalacji'. dired + to pakiet wypróbowany z Twoim kodem. – RNA

+0

Czy 'dired +' pojawia się po uruchomieniu 'package-list-packages'? Uważam, że będziesz musiał dodać marmoladę lub melpę do swoich "archiwów pakietów". Jeśli tak, możesz uruchomić '(package-install 'dired +)'? – ataylor

+0

Tak, pokazuje się, faktycznie już go zainstalowałem – RNA

92
; 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))) 
+6

Wolę: (lub (plik-istnieje-p pakiet-użytkownik-katalog) (odświeżanie zawartości pakietu) z zaakceptowanej odpowiedzi. Odświeżenie pakietu zwiększa czas uruchamiania w systemach, które już mają zainstalowane pakiety. Reszta tej odpowiedzi jest jednak idealna. – rfinz

+0

Wartość symbolu jako zmienna jest nieważna: package-archive-contents. Czy istnieje sposób, aby utworzyć listę w .emacs i użyć funkcji zdefiniowanej w nim, aby zainstalować wszystkie pakiety na liście (pomiń jeśli jest zainstalowany, zaktualizuj jeśli jest stary) jak Vundle dla Vima. Ponieważ nie chcę przesyłać wszystkich pakietów w elpa/na github, muszę to robić za każdym razem, gdy pakiet jest aktualizowany w 'pakiecie'. – CodyChan

+0

Co masz na myśli mówiąc @ rfinz? Wygląda na to, że 'refresh-refresh-contents' będzie uruchamiany tylko wtedy, gdy pakiet nie jest zainstalowany? W jaki sposób '' lub (file-exist-p package-user-dir)) 'lepiej/jak sprawdza nawet czy pakiety są zainstalowane? – Startec

16

Oto kod używam dla 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) 

Jeśli "Nie korzystam MELPA nie musisz tego wymagać (i jeśli musisz melpa.el musi być na twoim load-path (lub instalowany przez MELPA). Pakiet db nie jest odświeżany za każdym razem (ponieważ spowolniłoby to znacząco rozruch) - tylko w przypadku obecności odinstalowanych pakietów.

+0

Na podstawie twojej odpowiedzi, ve zmodyfikowałem to nieco i usunąłem użycie 'pętli' https://github.com/slipset/emacs/blob/master/ensure-packages.el – slipset

+0

Tak, ten przykład jest naprawdę bardziej skomplikowany, niż powinien. Kod, który obecnie używam w Prelude jest znacznie prostszy. –

7

Nikt jeszcze nie wspomniał o Cask, ale jest całkiem odpowiedni do tego zadania.

Zasadniczo tworzysz ~/.emacs.d/Cask listę pakietów, które chcesz zainstalować. Na przykład:

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

Running cask z wiersza polecenia można zainstalować te pakiety dla ciebie, a wszelkie zależności, czego potrzebują.

Ponadto można automatycznie aktualizować zainstalowane pakiety, używając cask update.

+0

Używam beczki w moich [dotfiles] (https://github.com/randomphrase/dotfiles) od jakiegoś czasu, działa świetnie. – Alastair

+0

Szkoda, że ​​Beczka wydaje się wymagać Pythona. Zastanawiam się, czy istnieje alternatywa tylko elisp? (To jest w pakiecie, oczywiście odpowiedzi na tej stronie spełniają wymóg elisp). –

+1

Skrypt Pythona jest cienkim opakowaniem po cask-cli.el, który możesz wywołać bezpośrednio, jeśli chcesz: '/ path/to/emacs - Q --script/path /to/cask/cask-cli.el - [argumentuje] ' – Alastair

3

Lubię sprawdzać, czy użytkownik chce najpierw zainstalować pakiety, jak to zrobiono w this answer. Odświeżam zawartość pakietu raz przed instalacją czegokolwiek. Nie jestem pewien, czy to jest najlepszy sposób, ale nie sądzę, że najlepsze odpowiedzi robiły to za mnie.

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

Emacs 25.1+ automatycznie śledzić pakiety zainstalowane przez użytkownika w konfigurowalny package-selected-packages zmiennej. package-install zaktualizuje zmienną niestandardową i będzie można zainstalować wszystkie wybrane pakiety za pomocą funkcji package-install-selected-packages.

Inną wygodną zaletą tego podejścia jest to, że można użyć package-autoremove, aby automatycznie usunąć pakiety, które nie są zawarte w package-selected-packages (choć zachowa to zależności).

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

Źródło: http://endlessparentheses.com/new-in-package-el-in-emacs-25-1-user-selected-packages.html

0

Oto moje, to krótszy :)

(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

wpadłem na problem, że nic się nie stało po dodaniu (package-install 'org) do .emacs. Chciałem zainstalować aktualną wersję org-mode, a wbudowany org-mode jest dość stary.

Wykopałem kod źródłowy z package-install z Emacs 25.3.1. Funkcja ja już sprawdza, czy pakiet jest zainstalowany, czy nie, i odmawia instalacji, jeśli pakiet jest już zainstalowany. Tak więc sprawdzanie (unless (package-installed-p package) ...) od odpowiedzi 10093312 jest w rzeczywistości niepoprawne.

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

Wbudowany org-mode liczy również zainstalowane i package-install odmawia zainstalować nowszą wersję z ELPA. Po spędzeniu trochę czasu na czytaniu pliku package.el, wymyśliłem następujące rozwiązanie.

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

Powodem jest to, że działa ona package-* funkcje rodziny obsługiwać argumenty różnie w zależności od tego, czy jeśli jest to symbol lub package-desc przedmiot. Możesz podać tylko informacje o wersji dla package-install za pośrednictwem obiektu package-desc.

Powiązane problemy