Refactor Doom core init process (again)

- Eager-load all core autoloaded libraries if autoloads file isn't
  present.
- Renames functions to be more descriptive of their true purpose:
  - doom-initialize-autoloads -> doom-load-autoloads-file
  - doom-load-env-vars -> doom-load-envvars-file
- Use doom-module-p instead of featurep! for backend use (the latter is
  mainly syntax sugar for module use, and evaluates at compile/expansion
  time, which may cause hash-table-p errors early in the startup
  process).
- Reorder plist library to prevent load order race condition with the
  functions using the macros that haven't been defined yet.
This commit is contained in:
Henrik Lissner 2019-07-22 22:28:43 +02:00
parent 23d111132a
commit 93f7520c79
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395
11 changed files with 109 additions and 107 deletions

View file

@ -61,8 +61,8 @@ It is useful to only pull in changes performed by 'doom refresh' on the command
line."
(interactive "P")
;; TODO regenerate autoloads
(doom-initialize-autoloads doom-autoload-file)
(doom-initialize-autoloads doom-package-autoload-file))
(doom-load-autoloads-file doom-autoload-file)
(doom-load-autoloads-file doom-package-autoload-file))
;;;###autoload
(defun doom/reload-env ()
@ -75,7 +75,7 @@ Uses the same mechanism as 'bin/doom env reload'."
(sit-for 1))
(unless (file-readable-p doom-env-file)
(error "Failed to generate env file"))
(doom-load-env-vars doom-env-file))
(doom-load-envvars-file doom-env-file))
;;;###autoload
(defun doom/reload-theme ()

View file

@ -1,8 +1,5 @@
;;; core/autoload/packages.el -*- lexical-binding: t; -*-
(require 'straight)
;;
;;; Package metadata

View file

@ -1,39 +1,5 @@
;;; ../work/conf/doom-emacs/core/autoload/plist.el -*- lexical-binding: t; -*-
;;;###autoload
(defun doom-plist-get (plist prop &optional nil-value)
"Return PROP in PLIST, if it exists. Otherwise NIL-VALUE."
(if-let (val (plist-member plist prop))
(cadr val)
nil-value))
;;;###autoload
(defun doom-plist-merge (from-plist to-plist)
"Destructively merge FROM-PLIST onto TO-PLIST"
(while plist
(plist-put! old-plist (pop plist) (pop plist))))
;;;###autoload
(defun doom-plist-delete-nil (plist)
"Delete `nil' properties from a copy of PLIST."
(let (p)
(while plist
(if (car plist)
(setq p (plist-put p (car plist) (nth 1 plist))))
(setq plist (cddr plist)))
p))
;;;###autoload
(defun doom-plist-delete (plist prop)
"Delete PROP from a copy of PLIST."
(let (p)
(while plist
(if (not (eq prop (car plist)))
(setq p (plist-put p (car plist) (nth 1 plist))))
(setq plist (cddr plist)))
p))
;;
;;; Macros
@ -82,3 +48,40 @@ BODY."
,plist-sym
,(doom-keyword-intern (symbol-name prop))))))
,@body)))
;;
;;; Library
;;;###autoload
(defun doom-plist-get (plist prop &optional nil-value)
"Return PROP in PLIST, if it exists. Otherwise NIL-VALUE."
(if-let (val (plist-member plist prop))
(cadr val)
nil-value))
;;;###autoload
(defun doom-plist-merge (from-plist to-plist)
"Destructively merge FROM-PLIST onto TO-PLIST"
(while plist
(plist-put! old-plist (pop plist) (pop plist))))
;;;###autoload
(defun doom-plist-delete-nil (plist)
"Delete `nil' properties from a copy of PLIST."
(let (p)
(while plist
(if (car plist)
(plist-put! p (car plist) (nth 1 plist)))
(setq plist (cddr plist)))
p))
;;;###autoload
(defun doom-plist-delete (plist prop)
"Delete PROP from a copy of PLIST."
(let (p)
(while plist
(if (not (eq prop (car plist)))
(plist-put! p (car plist) (nth 1 plist)))
(setq plist (cddr plist)))
p))

View file

@ -111,7 +111,7 @@ If DIR is not a project, it will be indexed (but not cached)."
(call-interactively
;; Intentionally avoid `helm-projectile-find-file', because it runs
;; asynchronously, and thus doesn't see the lexical `default-directory'
(if (featurep! :completion ivy)
(if (doom-module-p :completion 'ivy)
#'counsel-projectile-find-file
#'projectile-find-file)))
((fboundp 'project-find-file-in) ; emacs 26.1+ only
@ -127,8 +127,8 @@ If DIR is not a project, it will be indexed (but not cached)."
"Traverse a file structure starting linearly from DIR."
(let ((default-directory (file-truename (expand-file-name dir))))
(call-interactively
(cond ((featurep! :completion ivy)
(cond ((doom-module-p :completion 'ivy)
#'counsel-find-file)
((featurep! :completion helm)
((doom-module-p :completion 'helm)
#'helm-find-files)
(#'find-file)))))

View file

@ -270,7 +270,7 @@ Run this whenever your `doom!' block, or a module autoload file, is modified."
return t)))
(ignore
(print! (success "Skipping core autoloads, they are up-to-date"))
(doom-initialize-autoloads doom-autoload-file))
(doom-load-autoloads-file doom-autoload-file))
(print! (start "Regenerating core autoloads file"))
(if (doom-delete-autoloads-file doom-autoload-file)
@ -393,7 +393,7 @@ This should be run whenever your `doom!' block or update your packages."
return t)))
(ignore
(print! (success "Skipping package autoloads, they are up-to-date"))
(doom-initialize-autoloads doom-package-autoload-file))
(doom-load-autoloads-file doom-package-autoload-file))
(let (;; The following bindings are in `package-generate-autoloads'.
;; Presumably for a good reason, so I just copied them
(noninteractive t)

View file

@ -91,8 +91,8 @@ If RECOMPILE-P is non-nil, only recompile out-of-date files."
;; But first we must be sure that Doom and your private config have been
;; fully loaded. Which usually aren't so in an noninteractive session.
(doom-initialize-autoloads doom-autoload-file)
(doom-initialize-autoloads doom-package-autoload-file)
(doom-load-autoloads-file doom-autoload-file)
(doom-load-autoloads-file doom-package-autoload-file)
;;
(unless target-dirs

View file

@ -98,7 +98,7 @@ default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in
"#\n"
"# It is NOT safe to edit this file. Changes will be overwritten next time that\n"
"# `doom env refresh` is executed. Alternatively, create your own env file and\n"
"# load it with `(doom-load-env-vars FILE)`.\n"
"# load it with `(doom-load-envvars-file FILE)`.\n"
"#\n"
"# To auto-regenerate this file when `doom reload` is run, use `doom env auto' or\n"
"# set DOOMENV=1 in your shell environment/config.\n"

View file

@ -16,8 +16,8 @@ current directory."
(patterns nil)
(args command-line-args-left)
(doom-modules (doom-modules)))
(doom-initialize-autoloads doom-autoload-file)
(doom-initialize-autoloads doom-package-autoload-file)
(doom-load-autoloads-file doom-autoload-file)
(doom-load-autoloads-file doom-package-autoload-file)
(while args
(cond
;; ((member (car args) '("--traceback"))

View file

@ -144,13 +144,6 @@ BODY will be run when this dispatcher is called."
;;
;;; Dispatch commands
;; Eagerly load these libraries because this module may be loaded in a session
;; that hasn't been fully initialized (where autoloads files haven't been
;; generated or `load-path' populated).
(load! "autoload/format")
(load! "autoload/packages")
;; Load all of our subcommands
(def-command! (refresh re) (&optional force-p)
"Ensure Doom is properly set up.

View file

@ -1,7 +1,5 @@
;;; core/core-packages.el -*- lexical-binding: t; -*-
(require 'core-modules)
;; Emacs package management is opinionated, and so is Doom. Doom uses `straight'
;; to create a declarative, lazy-loaded and optionally rolling-release package
;; management system. We use `straight' over `package' because the latter is
@ -131,6 +129,7 @@ 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))
(doom-log "Initializing straight")
(setq doom-init-packages-p t)
(straight--reset-caches)
(mapc #'straight-use-recipes doom-core-package-sources)
@ -145,16 +144,14 @@ necessary package metadata is initialized and available for them."
(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))
;; On first install, the packages API will be unavailable
(unless (fboundp 'doom-package-list)
(load! "autoload/packages.el"))
(doom-log "Initializing 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)
do (cl-pushnew pkg doom-disabled-packages)
else if (not ignored)
do (with-demoted-errors "Package error: %s"
(straight-register-package
@ -176,7 +173,9 @@ necessary package metadata is initialized and available for them."
;; 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)
(if (or (featurep 'staight)
(load (file-name-sans-extension bootstrap-file) t t))
(cl-return-from 'straight t)
(unless (file-exists-p bootstrap-file)
(with-current-buffer
(url-retrieve-synchronously
@ -188,7 +187,7 @@ necessary package metadata is initialized and available for them."
(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))
(insert-file-contents-literally (doom-path straight-dir "build-cache.el"))
(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'."

View file

@ -430,7 +430,7 @@ If RETURN-P, return the message as a string instead of displaying it."
'window-setup-hook))
(run-hook-wrapped hook #'doom-try-run-hook)))
(defun doom-initialize-autoloads (file)
(defun doom-load-autoloads-file (file)
"Tries to load FILE (an autoloads file). Return t on success, throws an error
in interactive sessions, nil otherwise (but logs a warning)."
(condition-case e
@ -442,10 +442,11 @@ in interactive sessions, nil otherwise (but logs a warning)."
(message "Autoload file warning: %s -> %s" (car e) (error-message-string e))
(signal 'doom-autoload-error (list (file-name-nondirectory file) e))))))
(defun doom-load-env-vars (file)
(defun doom-load-envvars-file (file &optional noerror)
"Read and set envvars in FILE."
(if (not (file-readable-p file))
(signal 'file-error (list "Couldn't read envvar file" file))
(unless noerror
(signal 'file-error (list "Couldn't read envvar file" file)))
(with-temp-buffer
(insert-file-contents file)
(search-forward "\n\n" nil t)
@ -497,68 +498,77 @@ to least)."
(when (or force-p (not doom-init-p))
(setq doom-init-p t)
;; Reset as much state as possible
;; Reset as much state as possible, so `doom-initialize' can be treated like
;; a reset function. Particularly useful for reloading the config.
(setq exec-path doom--initial-exec-path
load-path doom--initial-load-path
process-environment doom--initial-process-environment)
(require 'core-lib)
(require 'core-modules)
(let (;; `doom-autoload-file' tells Emacs where to load all its functions
;; from. This includes everything in core/autoload/*.el and autoload
;; files in enabled modules.
(core-autoloads-p (doom-load-autoloads-file doom-autoload-file))
;; Loads `doom-package-autoload-file', which loads a concatenated
;; package autoloads file which caches `load-path', `auto-mode-alist',
;; `Info-directory-list', and `doom-disabled-packages'. A big
;; reduction in startup time.
(pkg-autoloads-p (doom-load-autoloads-file doom-package-autoload-file)))
;; `doom-autoload-file' tells Emacs where to load all its functions from.
;; This includes everything in core/autoload/*.el and autoload files in
;; enabled modules.
(when (or (not (doom-initialize-autoloads doom-autoload-file))
force-p)
(dolist (dir (list doom-local-dir doom-etc-dir doom-cache-dir doom-elpa-dir))
(unless (file-directory-p dir)
(make-directory dir 'parents-too)))
(if (and core-autoloads-p (not force-p))
;; In case we want to use package.el or straight via M-x
(progn
(after! (:or package straight)
(require 'core-packages))
(after! straight
(doom-initialize-packages)))
;; Ensure the package management system (and straight) are ready for
;; action (and all core packages/repos are installed)
(require 'core-packages)
(doom-ensure-straight)
(doom-initialize-packages force-p)
;; Eagerly load these libraries because this module may be loaded in a session
;; that hasn't been fully initialized (where autoloads files haven't been
;; generated or `load-path' populated).
(let ((default-directory doom-core-dir))
(dolist (file (file-expand-wildcards "autoload/*.el"))
(load file t t)))
(unless (or force-p noninteractive)
(user-error "Your doom autoloads are missing! Run `bin/doom refresh' to regenerate them")))
;; Create all our core directories to quell file errors
(dolist (dir (list doom-local-dir doom-etc-dir doom-cache-dir doom-elpa-dir))
(unless (file-directory-p dir)
(make-directory dir 'parents)))
;; Loads `doom-package-autoload-file', which loads a concatenated package
;; autoloads file which caches `load-path', `auto-mode-alist',
;; `Info-directory-list', and `doom-disabled-packages'. A big reduction in
;; startup time.
(unless (or force-p
(doom-initialize-autoloads doom-package-autoload-file)
noninteractive)
(user-error "Your package autoloads are missing! Run `bin/doom refresh' to regenerate them"))
;; Ensure the package management system (and straight) are ready for
;; action (and all core packages/repos are installed)
(require 'core-packages)
(doom-ensure-straight)
(doom-initialize-packages force-p))
;; Load shell environment
(when (and (not noninteractive)
(file-exists-p doom-env-file))
(doom-load-env-vars doom-env-file))))
(unless (or (and core-autoloads-p pkg-autoloads-p)
force-p
noninteractive)
(unless core-autoloads-p
(message "Your Doom core autoloads file is missing"))
(unless pkg-autoloads-p
(message "Your package autoloads file is missing"))
(user-error "Run `bin/doom refresh' to generate them")))
;; Load shell environment, optionally generated from 'doom env'
(unless noninteractive
(doom-load-envvars-file doom-env-file 'noerror))))
;;
;;; Bootstrap Doom
(doom-initialize noninteractive)
;; In case we want to use package.el's API
(with-eval-after-load 'package
(require 'core-packages))
;; Or straight interactively
(with-eval-after-load 'straight
(require 'core-packages)
(doom-initialize-packages))
(if noninteractive
(require 'core-cli)
(add-hook 'window-setup-hook #'doom-display-benchmark-h)
(require 'core-keybinds)
(require 'core-ui)
(require 'core-projects)
(require 'core-editor)
(doom-initialize-modules))
(require 'core-editor))
(doom-initialize-modules)
(provide 'core)
;;; core.el ends here