Bagaimana cara menginstal paket Emacs secara otomatis dengan menentukan daftar nama paket?

123

Saya menggunakan packageuntuk mengelola ekstensi Emacs saya. Untuk menyinkronkan pengaturan Emacs saya di komputer yang berbeda, saya ingin cara untuk menentukan daftar nama paket dalam .emacsfile dan kemudian packagedapat secara otomatis mencari dan menginstal paket, sehingga saya tidak perlu menginstalnya secara manual dengan menelepon M-x package-list-packages. Bagaimana cara melakukannya?

RNA
sumber
6
Jika Anda mengandalkan manajer paket untuk menginstal konfigurasi Anda, Anda mungkin ingin menentukan versi yang tepat (dan jika itu tidak memungkinkan, pertimbangkan untuk menyimpan semuanya di kontrol versi sendiri), karena jika tidak, Anda tidak dilindungi saat pustaka diperbarui dan mulai untuk konflik.
phils

Jawaban:

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
sumber
7
Saya lebih suka: (atau (file-existing-p package-user-dir) (package-refresh-content)) dari jawaban yang diterima. Refresh paket di sini meningkatkan waktu startup pada sistem yang sudah menginstal paket. Namun, sisa jawaban ini sempurna.
rfinz
Nilai simbol sebagai variabel tidak berlaku: paket-arsip-isi. Apakah ada cara agar saya dapat membuat daftar di .emacs dan menggunakan fungsi yang ditentukan di dalamnya untuk menginstal semua paket dalam daftar (lewati jika diinstal, perbarui jika lama) seperti Vundle untuk Vim. Karena saya tidak ingin mendorong semua paket di elpa / ke github, saya harus melakukannya setiap kali ada paket yang diperbarui package.
CodyChan
Apa maksudmu @rfinz? Sepertinya package-refresh-contentshanya akan berjalan jika paket tersebut tidak diinstal? Bagaimana (or (file-exists-p package-user-dir))lebih baik / bagaimana cara memeriksa apakah paket sudah diinstal?
Mulai
@Startec ya Anda benar! Ia memeriksa untuk melihat apakah direktori paket pengguna ada, dan jika tidak dijalankan package-refresh-contents. Ini mungkin hanya akan dijalankan saat pertama kali Anda membuka emacs di komputer baru, dan saya setuju dengan itu. Jika sebuah paket perlu diperbarui, itu dapat dilakukan secara manual.
rfinz
2
Jika Anda sudah menggunakan use-package, Anda dapat menggunakan :ensurekata kunci tersebut untuk menginstal paket secara otomatis. Ini juga mengatur package-selected-packagesjika Anda perlu mengakses daftar paket melalui customize atau programatically.
Nick McCurdy
45

Berdasarkan komentar Profpatsch dan jawaban di bawah ini:

(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
sumber
2
Apakah itu… peta dengan efek samping? Dan menyalahgunakan kemalasan or? Oh wow.
Profpatsch
1
Nah, mapcitu untuk efek sampingnya. Tapi kenapa tidak digunakan unless?
Profpatsch
Sebelumnya saya menggunakan kode ini dan kadang-kadang tidak berfungsi karena alasan yang tidak diketahui mengatakan "Paket bla-bla tidak tersedia untuk instalasi" (di sini bla-bla selalu elemen pertama dari daftar). Jika saya menginstal paket pertama secara manual, semuanya berfungsi dengan baik, tetapi ini bukan solusi. Bagaimanapun, jawaban dari Nicolas Dudebois berfungsi dengan baik.
avp
Saya perlu (package-initialize)sebelum referensi kepackage-user-dir
Frank Henard
3
Jadi di mana kita sebenarnya mendaftar paket yang ingin kita instal?
Andriy Drozdyuk
41

Emacs 25.1+ akan secara otomatis melacak paket yang diinstal pengguna dalam package-selected-packagesvariabel yang dapat disesuaikan . package-installakan memperbarui variabel kustomisasi, dan Anda dapat menginstal semua paket yang dipilih dengan package-install-selected-packagesfungsi tersebut.

Keuntungan praktis lainnya dari pendekatan ini adalah Anda dapat menggunakan package-autoremoveuntuk secara otomatis menghapus paket yang tidak termasuk di package-selected-packagesdalamnya (meskipun itu akan mempertahankan ketergantungan).

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

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

Nick McCurdy
sumber
17

Inilah kode yang saya gunakan untuk 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)

Jika Anda tidak menggunakan MELPA, Anda tidak perlu memerlukannya (dan jika Anda melakukannya melpa.elharus di load-path(atau diinstal melalui MELPA). Paket db tidak di-refresh setiap kali (karena ini akan memperlambat startup secara signifikan ) - hanya jika ada paket yang dihapus instalasinya.

Bozhidar Batsov
sumber
Berdasarkan jawaban Anda, saya telah mengubahnya sedikit dan menghapus penggunaan 'loop' github.com/slipset/emacs/blob/master/ensure-packages.el
slipset
Ya, contoh ini benar-benar lebih kompleks dari yang seharusnya. Kode yang saat ini saya gunakan di Prelude jauh lebih sederhana.
Bozhidar Batsov
7

Belum ada yang menyebutkan Cask , tapi cukup cocok untuk tugas ini.

Pada dasarnya Anda membuat ~/.emacs.d/Caskdaftar paket yang ingin Anda instal. Sebagai contoh:

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

Menjalankan caskdari baris perintah akan menginstal paket-paket ini untuk Anda, dan semua dependensi yang mereka butuhkan.

Selain itu, Anda dapat secara otomatis memperbarui paket yang diinstal menggunakan cask update.

Alastair
sumber
Saya telah menggunakan cask di dotfiles saya untuk beberapa waktu sekarang, berfungsi dengan baik.
Alastair
Sayang Cask sepertinya membutuhkan Python. Saya ingin tahu apakah ada alternatif khusus elisp? (Itu dalam satu paket; jelas jawaban di halaman ini memenuhi persyaratan elisp.)
Peter Jaric
1
Skrip python adalah pembungkus tipis di sekitar cask-cli.el, yang dapat Anda panggil secara langsung jika Anda suka:/path/to/emacs -Q --script /path/to/cask/cask-cli.el -- [args]
Alastair
Menarik! Apakah tidak mungkin menggunakannya dari dalam Emacs? Saya rasa itu karena ini juga merupakan alat pengembang, tetapi agak tidak biasa harus keluar dari Emacs ke CLI untuk mengelola Emacs.
Peter Jaric
4

Panggil package-installdengan nama paket sebagai simbol. Anda dapat menemukan nama paket untuk paket Anda dengan menelepon secara package-installinteraktif dan melengkapi namanya. Fungsinyapackage-installed-p akan memberi tahu Anda jika sudah diinstal.

Sebagai contoh:

(mapc
 (lambda (package)
   (or (package-installed-p package)
       (package-install package)))
 '(package1 package2 package3))
ataylor.dll
sumber
1
Terima kasih, tetapi saya mendapat kesalahan error: Package dired + 'tidak tersedia untuk instalasi`. dired + adalah paket yang saya coba dengan kode Anda.
RNA
Apakah dired+muncul saat Anda berlari package-list-packages? Saya yakin Anda harus menambahkan selai atau melpa ke Anda package-archives. Jika ya, bisakah kamu lari (package-install 'dired+)?
ataylor
Dalam hal ini, (package-installed-p 'dired+)harus dikembalikan tdan akan dilewati dalam kode di atas.
ataylor
Satu- package-installed-psatunya berfungsi dengan baik, tetapi seluruh blok kode tidak. Saya sudah mencoba beberapa paket.
RNA
2
Sepertinya pendahuluan dalam jawaban Nicolas Dudebout akan menyelesaikannya.
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
sumber
3

Saya suka memeriksa apakah pengguna ingin menginstal paket terlebih dahulu seperti yang dilakukan dalam jawaban ini . Juga saya menyegarkan konten paket saya sekali sebelum menginstal apa pun. Saya tidak yakin apakah ini cara terbaik, tetapi menurut saya jawaban teratas tidak melakukannya untuk saya.

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

Aku berlari ke dalam masalah yang tidak ada yang terjadi setelah menambahkan (package-install 'org)ke .emacs. Saya ingin menginstal versi terbaru dan versi org-modebawaannya org-modesudah cukup lama.

Saya menggali kode sumber package-installdari Emacs 25.3.1. Fungsi itu sendiri sudah memeriksa apakah sebuah paket diinstal atau tidak dan menolak untuk menginstalnya jika paket tersebut sudah diinstal. Jadi cek (unless (package-installed-p package) ...)dari jawaban 10093312 ternyata tidak beralasan.

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

Bawaan org-modejuga dihitung sebagai terpasang dan package-installmenolak untuk memasang versi yang lebih baru dari ELPA. Setelah menghabiskan waktu membaca package.el, saya menemukan solusi berikut.

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

Alasan mengapa ini berhasil adalah package-*fungsi keluarga menangani argumen secara berbeda berdasarkan apakah itu simbol atau package-descobjek. Anda hanya dapat menentukan info versi package-installmelalui sebuah package-descobjek.

Lei Zhao
sumber
0

Ini milikku, lebih pendek :)

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

Berikut cara lain.

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