Como instalar automaticamente os pacotes Emacs especificando uma lista de nomes de pacotes?

123

Estou usando packagepara gerenciar minhas extensões do Emacs. Para sincronizar minhas configurações do Emacs em computadores diferentes, gostaria de especificar uma lista de nomes de pacotes em .emacsarquivo e, em seguida, packageprocurar e instalar automaticamente os pacotes, para que eu não precise instalá-los manualmente ligando M-x package-list-packages. Como fazer isso?

RNA
fonte
6
Se você conta com o gerenciador de pacotes para instalar sua configuração, provavelmente desejará especificar as versões exatas (e, se isso não for possível, considere armazenar tudo no controle de versão), caso contrário você não estará protegido quando as bibliotecas forem atualizadas e iniciadas. entrar em conflito.
phils

Respostas:

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
fonte
7
Eu prefiro: (ou (arquivo-existe-p pacote-usuário-dir) (pacote-atualização-conteúdo)) da resposta aceita. A atualização do pacote aqui aumenta o tempo de inicialização em sistemas que já possuem os pacotes instalados. O resto desta resposta é perfeita, no entanto.
rfinz
O valor do símbolo como variável é nulo: package-archive-contents. Existe alguma maneira de eu fazer uma lista no .emacs e usar uma função definida nele para instalar todos os pacotes na lista (pule se estiver instalado, atualize se for antigo) como o Vundle for Vim. Como não quero enviar todos os pacotes no elpa / to github, tenho que fazer isso toda vez que um pacote é atualizado package.
CodyChan #
O que você quer dizer @rfinz? Parece package-refresh-contentsque só seria executado se o pacote não estiver instalado? Como é (or (file-exists-p package-user-dir))melhor / como ele verifica se os pacotes estão instalados?
Startec
@Startec sim, você está correto! Ele verifica se o diretório de pacotes do usuário existe e, se não existir, é executado package-refresh-contents. Provavelmente, isso só será executado na primeira vez que você abrir o emacs em um computador novo, e eu estou bem com isso. Se um pacote precisar de atualização, isso pode ser feito manualmente.
27416 rfinz
2
Se você já estiver usando use-package, poderá usar a :ensurepalavra-chave para instalar pacotes automaticamente. Isso também é configurado package-selected-packagesse você precisar acessar a lista de pacotes por meio de personalização ou programaticamente.
Nick McCurdy
45

Com base nos comentários de Profpatsch e respostas abaixo:

(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
fonte
2
Isso é ... um mapa com efeitos colaterais? E abusar da preguiça de or? Uau.
Profpatsch
1
Bem, mapcé para efeitos colaterais. Mas por que não usar unless?
Profpatsch
Anteriormente, usei esse código e, às vezes, ele não funcionava por algum motivo desconhecido, dizendo "O pacote blá-blá não está disponível para instalação" (aqui o blá-blá é sempre o primeiro elemento da lista). Se eu instalar o primeiro pacote manualmente, tudo funcionará bem, mas não é uma solução. De qualquer forma, a resposta de Nicolas Dudebois funciona bem.
avp
Eu precisava (package-initialize)antes da referência apackage-user-dir
Frank Henard 30/06
3
Então, onde realmente listamos os pacotes que queremos instalar?
Andriy Drozdyuk 31/07
41

O Emacs 25.1+ acompanhará automaticamente os pacotes instalados pelo usuário na package-selected-packagesvariável personalizável . package-installatualizará a variável customizada e você poderá instalar todos os pacotes selecionados com opackage-install-selected-packages função

Outra vantagem conveniente dessa abordagem é que você pode usar package-autoremovepara remover automaticamente os pacotes que não estão incluídos package-selected-packages(embora preservem as dependências).

(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

Nick McCurdy
fonte
17

Aqui está o código que eu uso para o 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 você não estiver usando o MELPA, não precisará solicitá-lo (e se precisar, melpa.eldeve estar no seu load-path(ou instalado via MELPA). O pacote db não é atualizado a cada vez (pois isso tornaria a inicialização significativamente mais lenta) ) - somente onde houver pacotes desinstalados presentes.

Bozhidar Batsov
fonte
Com base na sua resposta, eu a modifiquei um pouco e removi o uso de 'loop' github.com/slipset/emacs/blob/master/ensure-packages.el
slipset
Sim, este exemplo é realmente mais complexo do que precisa ser. O código que atualmente uso no Prelude é muito mais simples.
precisa saber é o seguinte
7

Ninguém mencionou Cask ainda, mas é bastante adequado para esta tarefa.

Basicamente, você cria ~/.emacs.d/Casklistando os pacotes que deseja instalar. Por exemplo:

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

A execução caskna linha de comando instalará esses pacotes para você e quaisquer dependências que eles precisem.

Além disso, você pode atualizar automaticamente os pacotes instalados usando cask update.

Alastair
fonte
Estou usando o barril nos meus arquivos de ponto há algum tempo, funciona muito bem.
Alastair
Uma pena Cask parece exigir Python. Gostaria de saber se existe uma alternativa apenas elisp? (Que está em um pacote, obviamente as respostas nesta página cumprir a exigência elisp.)
Peter Jaric
1
O script python é um invólucro fino em torno do cask-cli.el, que você pode chamar diretamente se quiser:/path/to/emacs -Q --script /path/to/cask/cask-cli.el -- [args]
Alastair
Interessante! Não é possível usá-lo de dentro do Emacs? Eu acho que é porque também é uma ferramenta de desenvolvimento, mas é meio incomum ter que sair do Emacs para uma CLI para gerenciar o Emacs.
precisa saber é o seguinte
4

Ligue package-installcom o nome do pacote como um símbolo. Você pode encontrar os nomes dos pacotes chamando package-installinterativamente e completando o nome. A função package-installed-pinformará se já foi instalada.

Por exemplo:

(mapc
 (lambda (package)
   (or (package-installed-p package)
       (package-install package)))
 '(package1 package2 package3))
ataylor
fonte
1
Obrigado, mas recebi um erro error: Package dired + 'não está disponível para instalação`. dired + é um pacote que tentei com seu código.
RNA
Aparece dired+quando você corre package-list-packages? Acredito que você precisará adicionar geléia ou melpa ao seu package-archives. Se sim, você pode correr (package-install 'dired+)?
precisa saber é
Nesse caso, (package-installed-p 'dired+)deve retornar te será ignorado no código acima.
precisa saber é
O package-installed-psozinho fina funciona, mas todo o bloco de código não. Eu tentei vários pacotes.
RNA
2
Parece que o prelúdio da resposta de Nicolas Dudebout resolverá isso.
Atletismo 10/04
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
fonte
3

Eu gosto de verificar se o usuário deseja instalar os pacotes primeiro, como feito nesta resposta . Também estou atualizando o conteúdo do meu pacote uma vez antes de instalar qualquer coisa. Não tenho certeza se essa é a melhor maneira, mas não acho que as principais respostas estejam fazendo isso por mim.

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

Corri para um problema que nada aconteceu depois de adicionar (package-install 'org)em .emacs. Eu queria instalar a versão atualizada org-modee o built-in org-modeé bastante antigo.

Eu descobri o código fonte package-installdo Emacs 25.3.1. A função self já verifica se um pacote está instalado ou não e se recusa a instalá-lo se o pacote já estiver instalado. Portanto, a verificação (unless (package-installed-p package) ...)da resposta 10093312 é de fato desnecessária.

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

O built-in org-modetambém conta como instalado e package-installse recusa a instalar a versão mais recente do ELPA. Depois de passar algum tempo lendo o package.el, criei a seguinte solução.

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

A razão pela qual funciona é que as package-*funções da família lidam com os argumentos de maneira diferente, dependendo se é um símbolo ou um package-descobjeto. Você só pode especificar informações de versão por package-installmeio de um package-descobjeto.

Lei Zhao
fonte
0

Aqui está o meu, é mais curto :)

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

Aqui está outra maneira.

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