วิธีการติดตั้งแพ็คเกจ Emacs โดยอัตโนมัติโดยระบุรายชื่อแพ็คเกจ?


123

ฉันใช้packageเพื่อจัดการส่วนขยาย Emacs ของฉัน เพื่อที่จะประสานการตั้งค่าของฉัน Emacs บนเครื่องคอมพิวเตอร์ที่แตกต่างกันผมต้องการวิธีการที่จะระบุรายชื่อแพคเกจในหนึ่ง.emacsไฟล์แล้วสามารถค้นหาโดยอัตโนมัติและติดตั้งแพคเกจเพื่อให้ฉันไม่จำเป็นต้องติดตั้งด้วยตนเองโดยการเรียกpackage M-x package-list-packagesต้องทำอย่างไร?


6
หากคุณใช้ตัวจัดการแพ็คเกจเพื่อติดตั้งการกำหนดค่าของคุณคุณอาจต้องการระบุเวอร์ชันที่แน่นอน (และหากเป็นไปไม่ได้ให้พิจารณาจัดเก็บทุกอย่างในการควบคุมเวอร์ชันด้วยตัวคุณเอง) มิฉะนั้นคุณจะไม่ได้รับการป้องกันเมื่อมีการอัปเดตไลบรารีและเริ่มต้น เพื่อความขัดแย้ง
Phils

คำตอบ:


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
ฉันชอบ: (หรือ (file-existing-p package-user-dir) (package-refresh-contents)) จากคำตอบที่ยอมรับ การรีเฟรชแพ็กเกจที่นี่จะเพิ่มเวลาเริ่มต้นระบบที่ติดตั้งแพ็กเกจไว้แล้ว ส่วนที่เหลือของคำตอบนี้สมบูรณ์แบบแม้ว่า
rfinz

ค่าของสัญลักษณ์เป็นตัวแปรถือเป็นโมฆะ: package-archive-contents มีวิธีใดบ้างที่ฉันสามารถสร้างรายการใน. emacs และใช้ฟังก์ชันที่กำหนดไว้ในนั้นเพื่อติดตั้งแพ็คเกจทั้งหมดในรายการ (ข้ามไปหากติดตั้งอัปเดตหากเก่า) เช่น Vundle for Vim เพราะผมไม่ต้องการที่จะผลักดันทุกแพคเกจใน ELPA / เพื่อ GitHub packageผมต้องทำเวลาเป็นแพคเกจที่มีการปรับปรุงในทุก
CodyChan

คุณหมายถึงอะไร @rfinz? ดูเหมือนว่าpackage-refresh-contentsจะทำงานได้ก็ต่อเมื่อไม่ได้ติดตั้งแพ็คเกจ? เป็นวิธีการที่(or (file-exists-p package-user-dir))ดีกว่า / วิธีการไม่ได้แม้กระทั่งตรวจสอบว่ามีการติดตั้งแพคเกจ?
Startec

@Startec ใช่คุณถูกต้อง! package-refresh-contentsมันจะตรวจสอบเพื่อดูว่าไดเรกทอรีแพคเกจของผู้ใช้ที่มีอยู่และถ้ามันไม่ได้ทำงาน สิ่งนี้อาจจะถูกเรียกใช้ในครั้งแรกที่คุณเปิด emac บนคอมพิวเตอร์เครื่องใหม่เท่านั้นและฉันก็สบายดี หากแพ็คเกจต้องการการอัปเดตที่สามารถทำได้ด้วยตนเอง
rfinz

2
หากคุณใช้งานอยู่แล้วuse-packageคุณสามารถใช้:ensureคีย์เวิร์ดเพื่อติดตั้งแพ็กเกจโดยอัตโนมัติ นอกจากนี้ยังตั้งค่าpackage-selected-packagesหากคุณต้องการเข้าถึงรายการแพ็คเกจผ่านการปรับแต่งหรือโดยทางโปรแกรม
Nick McCurdy

45

ตามความคิดเห็นของ Profpatsch และคำตอบด้านล่าง:

(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
นั่นคือ…แผนที่ที่มีผลข้างเคียง? และใช้ความเกียจคร้านในทางที่ผิดor? โอ้ว้าว.
Profpatsch

1
ดีmapcสำหรับผลข้างเคียง แต่ทำไมไม่ใช้unless?
Profpatsch

ก่อนหน้านี้ฉันใช้รหัสนี้และบางครั้งก็ใช้งานไม่ได้ด้วยเหตุผลที่ไม่ทราบสาเหตุว่า "Package blah-blah ไม่สามารถติดตั้งได้" (ที่นี่ blah-blah เป็นองค์ประกอบแรกของรายการเสมอ) หากฉันติดตั้งแพ็คเกจแรกด้วยตนเองทุกอย่างจะทำงานได้ดี แต่ไม่ใช่วิธีแก้ปัญหา อย่างไรก็ตามคำตอบจาก Nicolas Dudebois ใช้งานได้ดี
avp

ฉันต้องการ(package-initialize)ก่อนการอ้างอิงถึงpackage-user-dir
Frank Henard

3
แล้วเราจะแสดงรายการแพ็คเกจที่เราต้องการติดตั้งที่ไหน?
Andriy Drozdyuk

41

Emacs 25.1+ จะติดตามแพ็คเกจที่ผู้ใช้ติดตั้งโดยอัตโนมัติในpackage-selected-packagesตัวแปรที่ปรับแต่งได้ package-installจะอัปเดตตัวแปรปรับแต่งและคุณสามารถติดตั้งแพ็คเกจที่เลือกทั้งหมดด้วยpackage-install-selected-packagesฟังก์ชัน

ข้อดีอีกอย่างที่สะดวกของวิธีนี้คือคุณสามารถใช้package-autoremoveเพื่อลบแพ็กเกจที่ไม่รวมอยู่ในโดยอัตโนมัติpackage-selected-packages(แม้ว่าจะรักษาการอ้างอิงไว้ก็ตาม)

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

ที่มา: http://endlessparentheses.com/new-in-package-el-in-emacs-25-1-user-selected-packages.html


17

นี่คือรหัสที่ฉันใช้สำหรับ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)

หากคุณไม่ได้ใช้ MELPA คุณก็ไม่จำเป็นต้องใช้มัน (และหากคุณต้องmelpa.elใช้งานload-path(หรือติดตั้งผ่าน MELPA) ฐานข้อมูลแพ็กเกจจะไม่รีเฟรชทุกครั้ง (เนื่องจากจะทำให้การเริ่มต้นช้าลงอย่างมาก ) - เฉพาะในกรณีที่มีแพ็คเกจที่ถอนการติดตั้งอยู่


จากคำตอบของคุณฉันได้แก้ไขเล็กน้อยและลบการใช้ 'loop' github.com/slipset/emacs/blob/master/ensure-packages.el
slipset

ใช่ตัวอย่างนี้ซับซ้อนกว่าที่จำเป็นจริงๆ โค้ดที่ฉันใช้ใน Prelude นั้นง่ายกว่ามาก
Bozhidar Batsov

7

ยังไม่มีใครพูดถึงCaskแต่มันค่อนข้างเหมาะกับงานนี้

โดยทั่วไปคุณจะสร้าง~/.emacs.d/Caskรายการแพ็คเกจที่คุณต้องการติดตั้ง ตัวอย่างเช่น:

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

การเรียกใช้caskจากบรรทัดคำสั่งจะติดตั้งแพ็คเกจเหล่านี้ให้คุณและการอ้างอิงใด ๆ ที่พวกเขาต้องการ

นอกจากนี้คุณสามารถอัปเดตแพ็คเกจที่ติดตั้งโดยอัตโนมัติโดยใช้cask updateไฟล์.


ฉันใช้ถังในdotfilesมาระยะหนึ่งแล้วใช้ได้ดีมาก
Alastair

Cask น่าเสียดายที่ต้องการ Python ฉันสงสัยว่ามีทางเลือกอื่นอย่างเดียวหรือไม่? (อยู่ในแพ็คเกจเห็นได้ชัดว่าคำตอบในหน้านี้ตรงตามข้อกำหนดของ elisp)
Peter Jaric

1
สคริปต์ python เป็นกระดาษห่อบาง ๆ รอบ ๆ cask-cli.el ซึ่งคุณสามารถเรียกใช้โดยตรงหากต้องการ:/path/to/emacs -Q --script /path/to/cask/cask-cli.el -- [args]
Alastair

! ที่น่าสนใจ ไม่สามารถใช้จากภายใน Emacs ได้หรือไม่? ฉันเดาว่ามันเป็นเพราะมันเป็นเครื่องมือสำหรับนักพัฒนาด้วย แต่มันเป็นเรื่องแปลกที่ต้องก้าวออกจาก Emacs ไปยัง CLI เพื่อจัดการ Emacs
Peter Jaric

4

เรียกpackage-installด้วยชื่อแพ็กเกจเป็นสัญลักษณ์ คุณสามารถค้นหาชื่อแพ็กเกจสำหรับแพ็กเกจของคุณได้โดยการโทรpackage-installแบบโต้ตอบและกรอกชื่อ ฟังก์ชั่นpackage-installed-pนี้จะแจ้งให้คุณทราบว่าได้ติดตั้งไปแล้วหรือไม่

ตัวอย่างเช่น:

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

1
ขอบคุณ แต่ฉันได้รับข้อผิดพลาดerror: Package dired + 'ไม่สามารถติดตั้งได้' dired + เป็นแพ็คเกจที่ฉันลองใช้กับรหัสของคุณ
RNA

ไม่dired+แสดงขึ้นเมื่อคุณเรียกใช้package-list-packages? ฉันเชื่อว่าคุณจะต้องเพิ่มมาร์มาเลดหรือเมลปาลงในpackage-archivesไฟล์. ถ้าเป็นเช่นนั้นคุณสามารถวิ่งได้(package-install 'dired+)หรือไม่?
ataylor

ในกรณีนั้น(package-installed-p 'dired+)ควรส่งคืนtและจะข้ามไปในรหัสด้านบน
ataylor

package-installed-pคนเดียวทำงานได้ แต่บล็อกทั้งหมดของรหัสไม่ได้ ฉันลองหลายแพ็คเกจแล้ว
RNA

2
ดูเหมือนว่าการโหมโรงในคำตอบของ Nicolas Dudebout จะแก้ปัญหานั้นได้
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

ฉันชอบตรวจสอบว่าผู้ใช้ต้องการติดตั้งแพ็คเกจก่อนตามที่ทำในคำตอบนี้หรือไม่ นอกจากนี้ฉันกำลังรีเฟรชเนื้อหาแพ็คเกจหนึ่งครั้งก่อนที่จะติดตั้งอะไรก็ตาม ฉันไม่แน่ใจว่านี่เป็นวิธีที่ดีที่สุดหรือไม่ แต่ฉันไม่คิดว่าคำตอบยอดนิยมกำลังทำเพื่อฉัน

(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

ฉันวิ่งเข้าไปในปัญหาว่าไม่มีอะไรเกิดขึ้นหลังจากที่เพิ่มเข้ามา(package-install 'org) .emacsฉันต้องการติดตั้งเวอร์ชันล่าสุดorg-modeและในตัวorg-modeค่อนข้างเก่า

ฉันขุดซอร์สโค้ดของpackage-installจาก Emacs 25.3.1 ฟังก์ชั่นตรวจสอบตัวเองอยู่แล้วว่ามีการติดตั้งแพ็กเกจหรือไม่และปฏิเสธที่จะติดตั้งหากติดตั้งแพ็กเกจแล้ว ดังนั้นการตรวจสอบ(unless (package-installed-p package) ...)จากคำตอบ10093312จึงไม่ถูกเรียกสำหรับ

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

ในตัวorg-modeยังนับว่าติดตั้งแล้วและpackage-installปฏิเสธที่จะติดตั้งเวอร์ชันที่ใหม่กว่าจาก ELPA หลังจากใช้เวลาอ่าน package.el สักพักฉันก็ได้วิธีแก้ไขปัญหาต่อไปนี้

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

เหตุผลที่ใช้งานได้คือpackage-*ฟังก์ชันครอบครัวจัดการอาร์กิวเมนต์ต่างกันขึ้นอยู่กับว่าเป็นสัญลักษณ์หรือpackage-descวัตถุ คุณสามารถระบุข้อมูลเวอร์ชันpackage-installผ่านทางpackage-descออบเจ็กต์เท่านั้น


0

นี่ของฉันมันสั้นกว่า :)

(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

นี่เป็นอีกวิธีหนึ่ง

;; 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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.