doomemacs/core/core-packages.el

262 lines
11 KiB
EmacsLisp
Raw Normal View History

;;; core/core-packages.el -*- lexical-binding: t; -*-
2017-02-19 19:59:55 +08:00
(require 'core-modules)
;; 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-07-14 21:23:12 +08:00
;; The three key commands are:
;;
;; + `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 21:23:12 +08:00
;;
;; This system reads packages.el files located in each activated module (and one
2017-11-14 01:03:36 +08:00
;; in `doom-core-dir'). These contain `package!' blocks that tell DOOM what
2017-07-14 21:23:12 +08:00
;; plugins to install and where from.
2017-01-17 12:15:48 +08:00
;;
;; Why all the trouble? Because:
;; 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 13:13:24 +08:00
;;
;; You should be able to use package.el commands without any conflicts.
2017-02-06 13:13:24 +08:00
;;
;; See core/autoload/packages.el for more functions.
2017-01-17 12:15:48 +08:00
(defvar doom-init-packages-p nil
"If non-nil, Doom's package management system has been initialized.")
2017-06-05 20:21:52 +08:00
(defvar doom-packages ()
"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
`package!' declaration. Set by `doom-initialize-packages'.")
(defvar doom-core-packages '(straight use-package async)
"A list of packages that must be installed (and will be auto-installed if
missing) and shouldn't be deleted.")
(defvar doom-core-package-sources
'((org-elpa :local-repo nil)
(melpa
:type git :host github
:repo "melpa/melpa"
:no-build t)
(gnu-elpa-mirror
:type git :host github
:repo "emacs-straight/gnu-elpa-mirror"
:no-build t)
(emacsmirror-mirror
:type git :host github
:repo "emacs-straight/emacsmirror-mirror"
:no-build t))
"A list of recipes for straight's recipe repos.")
(defvar doom-disabled-packages ()
"A list of packages that should be ignored by `def-package!' and `after!'.")
;;
;;; Package managers
;; Ensure that, if we do need package.el, it is configured correctly. You really
;; shouldn't be using it, but it may be convenient for quick package testing.
(setq package--init-file-ensured t
2017-01-17 12:15:48 +08:00
package-enable-at-startup nil
package-user-dir doom-elpa-dir
package-gnupghome-dir (expand-file-name "gpg" doom-elpa-dir)
2017-02-11 13:46:42 +08:00
;; I omit Marmalade because its packages are manually submitted rather
;; than pulled, so packages are often out of date with upstream.
package-archives
(let ((proto (if gnutls-verify-error "http" "https")))
`(("gnu" . ,(concat proto "://elpa.gnu.org/packages/"))
("melpa" . ,(concat proto "://melpa.org/packages/"))
("org" . ,(concat proto "://orgmode.org/elpa/")))))
;; Don't save `package-selected-packages' to `custom-file'
(def-advice! doom--package-inhibit-custom-file-a (&optional value)
:override #'package--save-selected-packages
(if value (setq package-selected-packages value)))
;;; straight
(setq straight-cache-autoloads nil ; we already do this, and better.
;; Doom doesn't encourage you to modify packages in place. Disabling this
;; makes 'doom refresh' instant (once everything set up), which is much
;; nicer UX than the several seconds modification checks add.
straight-check-for-modifications nil
;; We do this ourselves, and a little more comprehensively.
straight-enable-package-integration nil
;; Before switching to straight, `doom-local-dir' would average out at
;; around 100mb. Afterwards, at around 1gb. With shallow cloning, that is
;; reduced to ~400mb. This imposes an isuse with packages that require
;; their git history for certain things to work (like magit and org), but
;; we're prepared for that.
straight-vc-git-default-clone-depth 1
;; Straight's own emacsmirror mirro is a little smaller and faster.
straight-recipes-emacsmirror-use-mirror t
;; Prefix declarations are unneeded bulk added to our autoloads file. Best
;; we just don't have to deal with them at all.
autoload-compute-prefixes nil)
;; Straight is hardcoded to operate out of ~/.emacs.d/straight. Not on my watch!
(def-advice! doom--straight-use-local-dir-a (orig-fn &rest args)
:around #'straight--emacs-dir
(let ((user-emacs-directory doom-local-dir))
(apply orig-fn args)))
;;
;;; Bootstrapper
(defun doom-initialize-packages (&optional force-p)
"Ensures that Doom's package system and straight.el are initialized.
If FORCE-P is non-nil, do it anyway.
This ensure `doom-packages' is populated, if isn't aren't already. Use this
before any of straight's or Doom's package management's API to ensure all the
necessary package metadata is initialized and available for them."
(when (or force-p (not doom-init-packages-p))
(setq doom-init-packages-p t)
(straight--reset-caches)
(mapc #'straight-use-recipes doom-core-package-sources)
(straight-register-package
`(straight :type git :host github
:repo ,(format "%s/straight.el" straight-repository-user)
:files ("straight*.el")
:branch ,straight-repository-branch))
(mapc #'straight-use-package doom-core-packages)
(when noninteractive
(add-hook 'kill-emacs-hook #'straight--transaction-finalize))
(dolist (package (straight--directory-files (straight--build-dir)))
(add-to-list 'load-path (directory-file-name (straight--build-dir package)))))
(when (or force-p (not doom-packages))
(setq doom-disabled-packages nil
doom-packages (doom-package-list))
(cl-loop for (pkg . plist) in doom-packages
for ignored = (eval (plist-get plist :ignore) t)
for disabled = (eval (plist-get plist :disable) t)
if disabled
do (add-to-list 'doom-disabled-packages pkg)
else if (not ignored)
do (with-demoted-errors "Package error: %s"
(straight-register-package
(if-let (recipe (plist-get plist :recipe))
`(,pkg ,@recipe)
pkg))))))
(defun doom-ensure-straight ()
"Ensure `straight' is installed and was compiled with this version of Emacs."
(defvar bootstrap-version)
(let* ((straight-dir (expand-file-name "straight/" doom-local-dir))
(bootstrap-file (expand-file-name "repos/straight.el/straight.el" straight-dir))
(bootstrap-version 5)
;; Force straight to install into ~/.emacs.d/.local/straight instead of
;; ~/.emacs.d/straight by pretending `doom-local-dir' is our .emacs.d.
(user-emacs-directory doom-local-dir))
(cl-block 'straight
;; Straight will throw `emacs-version-changed' if it's loaded with a
;; version of Emacs that doesn't match the one it was compiled with.
;; Getting this error isn't very good UX...
(catch 'emacs-version-changed
(unless (require 'straight nil t)
(unless (file-exists-p bootstrap-file)
(with-current-buffer
(url-retrieve-synchronously
"https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
'silent 'inhibit-cookies)
(goto-char (point-max))
(eval-print-last-sexp)))
(load bootstrap-file nil 'nomessage))
(cl-return-from 'straight t))
;; ...so we transform it into a more graceful error message:
(with-temp-buffer
(insert-file-contents-literally (expand-file-name "build-cache.el" straight-dir))
(let ((_ (read (current-buffer)))
(last-emacs-version (read (current-buffer))))
(user-error "Your version of Emacs has changed (from %S to %S). You must rebuild your packages with 'doom rebuild'."
emacs-version last-emacs-version))))))
2017-02-11 19:52:38 +08:00
2017-02-06 13:13:24 +08:00
2017-02-11 13:46:42 +08:00
;;
;;; Module package macros
2017-01-17 12:15:48 +08:00
(cl-defmacro package! (name &rest plist &key built-in _recipe disable ignore _freeze)
"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.
Only use this macro in a module's packages.el file.
2017-02-11 13:46:42 +08:00
Accepts the following properties:
:recipe RECIPE
Takes a MELPA-style recipe (see `quelpa-recipe' in `quelpa' for an example);
for packages to be installed from external sources.
:disable BOOL
Do not install or update this package AND disable all of its `def-package!'
blocks.
:ignore FORM
2018-03-27 06:15:03 +08:00
Do not install this package.
:freeze FORM
Do not update this package if FORM is non-nil.
:built-in BOOL
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.
Returns t if package is successfully registered, and nil if it was disabled
elsewhere."
2017-01-17 12:15:48 +08:00
(declare (indent defun))
(let ((old-plist (cdr (assq name doom-packages))))
(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))))
(when built-in
2019-07-06 04:28:55 +08:00
(doom-log "Ignoring built-in package %S" name)
(when (equal built-in '(quote prefer))
(setq built-in `(locate-library ,(symbol-name name) nil doom--initial-load-path))))
2019-07-06 04:28:55 +08:00
(setq plist (plist-put plist :ignore (or built-in ignore)))
(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)
;; TODO Add `straight-use-package-pre-build-function' support
(macroexp-progn
(append `((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))))))
(defmacro disable-packages! (&rest packages)
:boom: revise advice naming convention (1/2) This is first of three big naming convention updates that have been a long time coming. With 2.1 on the horizon, all the breaking updates will batched together in preparation for the long haul. In this commit, we do away with the asterix to communicate that a function is an advice function, and we replace it with the '-a' suffix. e.g. doom*shut-up -> doom-shut-up-a doom*recenter -> doom-recenter-a +evil*static-reindent -> +evil--static-reindent-a The rationale behind this change is: 1. Elisp's own formatting/indenting tools would occasionally struggle with | and * (particularly pp and cl-prettyprint). They have no problem with / and :, fortunately. 2. External syntax highlighters (like pygmentize, discord markdown or github markdown) struggle with it, sometimes refusing to highlight code beyond these symbols. 3. * and | are less expressive than - and -- in communicating the intended visibility, versatility and stability of a function. 4. It complicated the regexps we must use to search for them. 5. They were arbitrary and over-complicated to begin with, decided on haphazardly way back when Doom was simply "my private config". Anyhow, like how predicate functions have the -p suffix, we'll adopt the -a suffix for advice functions, -h for hook functions and -fn for variable functions. Other noteable changes: - Replaces advice-{add,remove}! macro with new def-advice! macro. The old pair weren't as useful. The new def-advice! saves on a lot of space. - Removed "stage" assertions to make sure you were using the right macros in the right place. Turned out to not be necessary, we'll employ better checks later.
2019-07-18 21:42:52 +08:00
"A convenience macro for disabling packages in bulk.
Only use this macro in a module's (or your private) packages.el file."
(macroexp-progn
(cl-loop for p in packages
collect `(package! ,p :disable t))))
2017-01-17 12:15:48 +08:00
(provide 'core-packages)
;;; core-packages.el ends here