How to automatically install Emacs packages by specifying a list of package names?

后端 未结 11 2611
隐瞒了意图╮
隐瞒了意图╮ 2020-12-22 15:22

I am using package to manage my Emacs extensions. In order to synchronize my Emacs settings on different computers, I\'d like a way to specify a list of package

11条回答
  •  挽巷
    挽巷 (楼主)
    2020-12-22 15:49

    I ran into a problem that nothing happened after adding (package-install 'org) into .emacs. I wanted to install the up-to-date version of org-mode and the built-in org-mode is quite old.

    I dug out the source code of package-install from Emacs 25.3.1. The function self already checks if a package is installed or not and refuses to install it if the package is already installed. So the check (unless (package-installed-p package) ...) from answer 10093312 is in fact uncalled for.

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

    The built-in org-mode also counts as installed and package-install refuses to install the newer version from ELPA. After spending some time reading package.el, I came up with the following solution.

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

    The reason why it works is that package-* family functions handle the arguments differently based on whether if it is a symbol or a package-desc object. You can only specify version info for package-install via a package-desc object.

提交回复
热议问题