2018-06-11 23:18:15 +02:00
|
|
|
;;; core/core-packages.el -*- lexical-binding: t; -*-
|
2017-02-19 06:59:55 -05:00
|
|
|
|
2018-05-24 21:17:23 +02:00
|
|
|
;; Emacs package management is opinionated, and so am I. I've bound together
|
|
|
|
;; `use-package', `quelpa' and package.el to create my own, rolling-release,
|
|
|
|
;; lazily-loaded package management system for Emacs.
|
2017-02-11 06:00:08 -05:00
|
|
|
;;
|
2017-07-14 15:23:12 +02:00
|
|
|
;; The three key commands are:
|
|
|
|
;;
|
2018-06-17 21:35:58 +02:00
|
|
|
;; + `bin/doom install`: Installs packages that are wanted, but not installed.
|
|
|
|
;; + `bin/doom update`: Updates packages that are out-of-date.
|
|
|
|
;; + `bin/doom autoremove`: Uninstalls packages that are no longer needed.
|
2017-07-14 15:23:12 +02:00
|
|
|
;;
|
|
|
|
;; This system reads packages.el files located in each activated module (and one
|
2017-11-13 18:03:36 +01:00
|
|
|
;; in `doom-core-dir'). These contain `package!' blocks that tell DOOM what
|
2017-07-14 15:23:12 +02:00
|
|
|
;; plugins to install and where from.
|
2017-01-16 23:15:48 -05:00
|
|
|
;;
|
2017-01-28 02:02:16 -05:00
|
|
|
;; Why all the trouble? Because:
|
2019-05-12 22:09:52 -04:00
|
|
|
;; 1. *Scriptability:* I live in the command line. I want a shell-scriptable
|
|
|
|
;; interface for updating and installing Emacs packages.
|
|
|
|
;; 2. *Reach:* I want packages from sources other than ELPA (like github or
|
|
|
|
;; gitlab). Some plugins are out-of-date through official channels, have
|
|
|
|
;; changed hands, have a superior fork, or simply aren't available in ELPA
|
|
|
|
;; repos.
|
|
|
|
;; 3. *Performance:* The package management system isn't loaded until you use
|
|
|
|
;; the package management API. Not having to initialize package.el or quelpa
|
|
|
|
;; (and check that your packages are installed) every time you start up (or
|
|
|
|
;; load a package) speeds things up a great deal.
|
|
|
|
;; 4. *Separation of concerns:* It's more organized and reduces cognitive load
|
|
|
|
;; to separate configuring of packages and installing/updating them.
|
2017-02-06 00:13:24 -05:00
|
|
|
;;
|
2018-03-14 18:25:22 -04:00
|
|
|
;; You should be able to use package.el commands without any conflicts.
|
2017-02-06 00:13:24 -05:00
|
|
|
;;
|
|
|
|
;; See core/autoload/packages.el for more functions.
|
2017-01-16 23:15:48 -05:00
|
|
|
|
2017-06-05 14:21:52 +02:00
|
|
|
(defvar doom-packages ()
|
2017-02-11 06:00:08 -05:00
|
|
|
"A list of enabled packages. Each element is a sublist, whose CAR is the
|
|
|
|
package's name as a symbol, and whose CDR is the plist supplied to its
|
2017-02-23 00:06:12 -05:00
|
|
|
`package!' declaration. Set by `doom-initialize-packages'.")
|
2017-02-11 06:00:08 -05:00
|
|
|
|
2019-03-28 13:15:08 -04:00
|
|
|
(defvar doom-core-packages
|
2019-05-17 20:19:35 -04:00
|
|
|
'(persistent-soft use-package quelpa async)
|
2017-02-11 06:00:08 -05:00
|
|
|
"A list of packages that must be installed (and will be auto-installed if
|
|
|
|
missing) and shouldn't be deleted.")
|
|
|
|
|
2017-06-05 16:45:42 +02:00
|
|
|
(defvar doom-disabled-packages ()
|
2019-05-13 19:29:38 -04:00
|
|
|
"A list of packages that should be ignored by `def-package!' and `after!'.")
|
2017-06-05 16:45:42 +02:00
|
|
|
|
2019-05-12 22:09:52 -04:00
|
|
|
;;; package.el
|
2018-06-11 23:18:15 +02:00
|
|
|
(setq package--init-file-ensured t
|
2017-01-16 23:15:48 -05:00
|
|
|
package-user-dir (expand-file-name "elpa" doom-packages-dir)
|
2018-06-20 12:45:51 +02:00
|
|
|
package-gnupghome-dir (expand-file-name "gpg" doom-packages-dir)
|
2017-01-16 23:15:48 -05:00
|
|
|
package-enable-at-startup nil
|
2017-02-11 00:46:42 -05:00
|
|
|
;; I omit Marmalade because its packages are manually submitted rather
|
|
|
|
;; than pulled, so packages are often out of date with upstream.
|
2019-05-12 22:09:52 -04:00
|
|
|
package-archives
|
|
|
|
`(("gnu" . "https://elpa.gnu.org/packages/")
|
|
|
|
("melpa" . "https://melpa.org/packages/")
|
2019-07-13 13:58:03 +02:00
|
|
|
("org" . "https://orgmode.org/elpa/")))
|
2019-05-12 22:09:52 -04:00
|
|
|
|
2019-07-02 23:13:01 +02:00
|
|
|
;; Don't save `package-selected-packages' to `custom-file'
|
|
|
|
(advice-add #'package--save-selected-packages :override
|
|
|
|
(lambda (&optional value) (if value (setq package-selected-packages value))))
|
|
|
|
|
2019-05-12 22:09:52 -04:00
|
|
|
(when (or (not gnutls-verify-error)
|
|
|
|
(not (ignore-errors (gnutls-available-p))))
|
|
|
|
(dolist (archive package-archives)
|
|
|
|
(setcdr archive (replace-regexp-in-string "^https://" "http://" (cdr archive) t nil))))
|
|
|
|
|
|
|
|
;;; quelpa
|
|
|
|
(setq quelpa-dir (expand-file-name "quelpa" doom-packages-dir)
|
|
|
|
quelpa-verbose doom-debug-mode
|
2017-01-16 23:15:48 -05:00
|
|
|
|
2017-06-05 16:45:19 +02:00
|
|
|
;; Don't track MELPA, we'll use package.el for that
|
2017-01-16 23:15:48 -05:00
|
|
|
quelpa-checkout-melpa-p nil
|
|
|
|
quelpa-update-melpa-p nil
|
2017-02-19 06:59:55 -05:00
|
|
|
quelpa-melpa-recipe-stores nil
|
2019-05-12 22:09:52 -04:00
|
|
|
quelpa-self-upgrade-p nil)
|
2018-05-14 15:57:54 +02:00
|
|
|
|
|
|
|
|
|
|
|
;;
|
2019-05-12 22:09:52 -04:00
|
|
|
;;; Bootstrapper
|
2018-05-14 15:57:54 +02:00
|
|
|
|
2018-03-02 17:45:15 -05:00
|
|
|
(defun doom-initialize-packages (&optional force-p)
|
2018-05-20 00:01:07 +02:00
|
|
|
"Ensures that Doom's package management system, package.el and quelpa are
|
|
|
|
initialized, and `doom-packages', `packages-alist' and `quelpa-cache' are
|
|
|
|
populated, if they aren't already.
|
2018-03-02 17:45:15 -05:00
|
|
|
|
2018-05-20 00:01:07 +02:00
|
|
|
If FORCE-P is non-nil, do it anyway.
|
|
|
|
If FORCE-P is 'internal, only (re)populate `doom-packages'.
|
2018-03-02 17:45:15 -05:00
|
|
|
|
|
|
|
Use this before any of package.el, quelpa or Doom's package management's API to
|
|
|
|
ensure all the necessary package metadata is initialized and available for
|
|
|
|
them."
|
2019-03-06 00:26:33 -05:00
|
|
|
(let ((load-prefer-newer t)) ; reduce stale code issues
|
|
|
|
;; package.el and quelpa handle themselves if their state changes during the
|
2019-05-12 22:09:52 -04:00
|
|
|
;; current session, but if you change a packages.el file in a module,
|
2019-03-06 00:26:33 -05:00
|
|
|
;; there's no non-trivial way to detect that, so to reload only
|
2019-05-12 22:09:52 -04:00
|
|
|
;; `doom-packages' pass 'internal as FORCE-P or use `doom/reload-packages'.
|
2019-03-06 00:26:33 -05:00
|
|
|
(unless (eq force-p 'internal)
|
|
|
|
;; `package-alist'
|
|
|
|
(when (or force-p (not (bound-and-true-p package-alist)))
|
|
|
|
(doom-ensure-packages-initialized 'force)
|
|
|
|
(setq load-path (cl-delete-if-not #'file-directory-p load-path)))
|
|
|
|
;; `quelpa-cache'
|
|
|
|
(when (or force-p (not (bound-and-true-p quelpa-cache)))
|
|
|
|
;; ensure un-byte-compiled version of quelpa is loaded
|
|
|
|
(unless (featurep 'quelpa)
|
|
|
|
(load (locate-library "quelpa.el") nil t t))
|
|
|
|
(setq quelpa-initialized-p nil)
|
|
|
|
(or (quelpa-setup-p)
|
|
|
|
(error "Could not initialize quelpa"))))
|
|
|
|
;; `doom-packages'
|
|
|
|
(when (or force-p (not doom-packages))
|
|
|
|
(setq doom-packages (doom-package-list)))))
|
2018-03-02 17:45:15 -05:00
|
|
|
|
|
|
|
|
|
|
|
;;
|
2019-05-12 22:09:52 -04:00
|
|
|
;;; Package API
|
2018-03-02 17:45:15 -05:00
|
|
|
|
2018-06-11 23:18:15 +02:00
|
|
|
(defun doom-ensure-packages-initialized (&optional force-p)
|
|
|
|
"Make sure package.el is initialized."
|
|
|
|
(when (or force-p (not (bound-and-true-p package--initialized)))
|
|
|
|
(require 'package)
|
|
|
|
(setq package-activated-list nil
|
|
|
|
package--initialized nil)
|
|
|
|
(let (byte-compile-warnings)
|
|
|
|
(condition-case _
|
2018-09-27 23:45:32 -04:00
|
|
|
(package-initialize)
|
2018-06-11 23:18:15 +02:00
|
|
|
('error (package-refresh-contents)
|
|
|
|
(setq doom--refreshed-p t)
|
|
|
|
(package-initialize))))))
|
2018-05-18 01:21:09 +02:00
|
|
|
|
2018-06-11 23:18:15 +02:00
|
|
|
(defun doom-ensure-core-packages ()
|
|
|
|
"Make sure `doom-core-packages' are installed."
|
2019-06-25 21:38:16 +02:00
|
|
|
(when-let (core-packages (cl-remove-if #'package-installed-p doom-core-packages))
|
2018-06-11 23:18:15 +02:00
|
|
|
(message "Installing core packages")
|
|
|
|
(unless doom--refreshed-p
|
|
|
|
(package-refresh-contents))
|
|
|
|
(dolist (package core-packages)
|
|
|
|
(let ((inhibit-message t))
|
|
|
|
(package-install package))
|
|
|
|
(if (package-installed-p package)
|
|
|
|
(message "✓ Installed %s" package)
|
|
|
|
(error "✕ Couldn't install %s" package)))
|
|
|
|
(message "Installing core packages...done")))
|
2017-02-11 06:52:38 -05:00
|
|
|
|
2017-02-06 00:13:24 -05:00
|
|
|
|
2017-02-11 00:46:42 -05:00
|
|
|
;;
|
2018-03-02 17:45:15 -05:00
|
|
|
;; Module package macros
|
2017-01-16 23:15:48 -05:00
|
|
|
|
2019-07-05 22:28:55 +02:00
|
|
|
(cl-defmacro package! (name &rest plist &key built-in recipe pin disable ignore _freeze)
|
2017-07-02 16:47:02 +02:00
|
|
|
"Declares a package and how to install it (if applicable).
|
|
|
|
|
|
|
|
This macro is declarative and does not load nor install packages. It is used to
|
|
|
|
populate `doom-packages' with metadata about the packages Doom needs to keep
|
|
|
|
track of.
|
|
|
|
|
2018-04-03 15:00:52 -04:00
|
|
|
Only use this macro in a module's packages.el file.
|
2017-02-04 21:07:54 -05:00
|
|
|
|
2017-02-11 00:46:42 -05:00
|
|
|
Accepts the following properties:
|
2017-02-03 20:10:40 -05:00
|
|
|
|
2018-03-26 16:44:24 -04:00
|
|
|
:recipe RECIPE
|
|
|
|
Takes a MELPA-style recipe (see `quelpa-recipe' in `quelpa' for an example);
|
|
|
|
for packages to be installed from external sources.
|
|
|
|
:pin ARCHIVE-NAME
|
|
|
|
Instructs ELPA to only look for this package in ARCHIVE-NAME. e.g. \"org\".
|
|
|
|
Ignored if RECIPE is present.
|
|
|
|
:disable BOOL
|
|
|
|
Do not install or update this package AND disable all of its `def-package!'
|
|
|
|
blocks.
|
|
|
|
:ignore FORM
|
2018-03-26 18:15:03 -04:00
|
|
|
Do not install this package.
|
2018-03-26 16:44:24 -04:00
|
|
|
:freeze FORM
|
2018-05-15 23:11:48 +02:00
|
|
|
Do not update this package if FORM is non-nil.
|
2019-03-06 00:26:33 -05:00
|
|
|
:built-in BOOL
|
2019-07-05 20:03:37 +02:00
|
|
|
Same as :ignore if the package is a built-in Emacs package. If set to
|
|
|
|
'prefer, will use built-in package if it is present.
|
2018-05-15 23:11:48 +02:00
|
|
|
|
|
|
|
Returns t if package is successfully registered, and nil if it was disabled
|
|
|
|
elsewhere."
|
2017-01-16 23:15:48 -05:00
|
|
|
(declare (indent defun))
|
2018-05-14 18:40:35 +02:00
|
|
|
(doom--assert-stage-p 'packages #'package!)
|
2019-03-06 00:26:33 -05:00
|
|
|
(let ((old-plist (cdr (assq name doom-packages))))
|
2018-06-24 19:58:25 +02:00
|
|
|
(when recipe
|
|
|
|
(when (cl-evenp (length recipe))
|
|
|
|
(setq plist (plist-put plist :recipe (cons name recipe))))
|
|
|
|
(setq pin nil
|
|
|
|
plist (plist-put plist :pin nil)))
|
2019-03-06 00:26:33 -05:00
|
|
|
(let ((module-list (plist-get old-plist :modules))
|
|
|
|
(module (or doom--current-module
|
|
|
|
(let ((file (FILE!)))
|
|
|
|
(cond ((file-in-directory-p file doom-private-dir)
|
|
|
|
(list :private))
|
|
|
|
((file-in-directory-p file doom-core-dir)
|
|
|
|
(list :core))
|
|
|
|
((doom-module-from-path file)))))))
|
|
|
|
(unless (member module module-list)
|
|
|
|
(setq module-list (append module-list (list module) nil)
|
|
|
|
plist (plist-put plist :modules module-list))))
|
2019-06-11 08:01:42 +02:00
|
|
|
(when built-in
|
2019-07-05 22:28:55 +02:00
|
|
|
(doom-log "Ignoring built-in package %S" name)
|
2019-07-05 23:07:05 +02:00
|
|
|
(when (equal built-in '(quote prefer))
|
2019-07-05 23:51:39 +02:00
|
|
|
(setq built-in `(locate-library ,(symbol-name name) nil doom-site-load-path))))
|
2019-07-05 22:28:55 +02:00
|
|
|
(setq plist (plist-put plist :ignore (or built-in ignore)))
|
2019-03-06 00:26:33 -05:00
|
|
|
(while plist
|
|
|
|
(unless (null (cadr plist))
|
|
|
|
(setq old-plist (plist-put old-plist (car plist) (cadr plist))))
|
|
|
|
(pop plist)
|
|
|
|
(pop plist))
|
|
|
|
(setq plist old-plist)
|
2018-06-24 19:58:25 +02:00
|
|
|
(macroexp-progn
|
2019-05-02 21:54:47 -04:00
|
|
|
(append (when pin
|
2019-03-06 00:26:33 -05:00
|
|
|
(doom-log "Pinning package '%s' to '%s'" name pin)
|
|
|
|
`((setf (alist-get ',name package-pinned-packages) ,pin)))
|
2019-05-02 21:54:47 -04:00
|
|
|
`((setf (alist-get ',name doom-packages) ',plist))
|
|
|
|
(when disable
|
|
|
|
(doom-log "Disabling package '%s'" name)
|
|
|
|
`((add-to-list 'doom-disabled-packages ',name nil 'eq)
|
|
|
|
nil))))))
|
2017-01-28 02:02:16 -05:00
|
|
|
|
2018-03-26 02:58:22 -04:00
|
|
|
(defmacro packages! (&rest packages)
|
2019-04-21 19:35:27 -04:00
|
|
|
"A convenience macro for `package!' for declaring multiple packages at once.
|
2018-04-03 15:00:52 -04:00
|
|
|
|
|
|
|
Only use this macro in a module's packages.el file."
|
2018-05-14 18:40:35 +02:00
|
|
|
(doom--assert-stage-p 'packages #'packages!)
|
2019-01-02 14:11:02 -05:00
|
|
|
(macroexp-progn
|
|
|
|
(cl-loop for desc in packages
|
|
|
|
collect (macroexpand `(package! ,@(doom-enlist desc))))))
|
2018-03-26 02:58:22 -04:00
|
|
|
|
|
|
|
(defmacro disable-packages! (&rest packages)
|
|
|
|
"A convenience macro like `package!', but allows you to disable multiple
|
2018-04-03 15:00:52 -04:00
|
|
|
packages at once.
|
|
|
|
|
|
|
|
Only use this macro in a module's packages.el file."
|
2018-05-14 18:40:35 +02:00
|
|
|
(doom--assert-stage-p 'packages #'disable-packages!)
|
2019-01-02 13:17:26 -05:00
|
|
|
(macroexp-progn
|
|
|
|
(cl-loop for pkg in packages
|
2019-01-02 14:11:02 -05:00
|
|
|
collect (macroexpand `(package! ,pkg :disable t)))))
|
2018-03-26 02:58:22 -04:00
|
|
|
|
2017-01-16 23:15:48 -05:00
|
|
|
(provide 'core-packages)
|
|
|
|
;;; core-packages.el ends here
|