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." line."
(interactive "P") (interactive "P")
;; TODO regenerate autoloads ;; TODO regenerate autoloads
(doom-initialize-autoloads doom-autoload-file) (doom-load-autoloads-file doom-autoload-file)
(doom-initialize-autoloads doom-package-autoload-file)) (doom-load-autoloads-file doom-package-autoload-file))
;;;###autoload ;;;###autoload
(defun doom/reload-env () (defun doom/reload-env ()
@ -75,7 +75,7 @@ Uses the same mechanism as 'bin/doom env reload'."
(sit-for 1)) (sit-for 1))
(unless (file-readable-p doom-env-file) (unless (file-readable-p doom-env-file)
(error "Failed to generate env file")) (error "Failed to generate env file"))
(doom-load-env-vars doom-env-file)) (doom-load-envvars-file doom-env-file))
;;;###autoload ;;;###autoload
(defun doom/reload-theme () (defun doom/reload-theme ()

View file

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

View file

@ -1,39 +1,5 @@
;;; ../work/conf/doom-emacs/core/autoload/plist.el -*- lexical-binding: t; -*- ;;; ../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 ;;; Macros
@ -82,3 +48,40 @@ BODY."
,plist-sym ,plist-sym
,(doom-keyword-intern (symbol-name prop)))))) ,(doom-keyword-intern (symbol-name prop))))))
,@body))) ,@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 (call-interactively
;; Intentionally avoid `helm-projectile-find-file', because it runs ;; Intentionally avoid `helm-projectile-find-file', because it runs
;; asynchronously, and thus doesn't see the lexical `default-directory' ;; asynchronously, and thus doesn't see the lexical `default-directory'
(if (featurep! :completion ivy) (if (doom-module-p :completion 'ivy)
#'counsel-projectile-find-file #'counsel-projectile-find-file
#'projectile-find-file))) #'projectile-find-file)))
((fboundp 'project-find-file-in) ; emacs 26.1+ only ((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." "Traverse a file structure starting linearly from DIR."
(let ((default-directory (file-truename (expand-file-name dir)))) (let ((default-directory (file-truename (expand-file-name dir))))
(call-interactively (call-interactively
(cond ((featurep! :completion ivy) (cond ((doom-module-p :completion 'ivy)
#'counsel-find-file) #'counsel-find-file)
((featurep! :completion helm) ((doom-module-p :completion 'helm)
#'helm-find-files) #'helm-find-files)
(#'find-file))))) (#'find-file)))))

View file

@ -270,7 +270,7 @@ Run this whenever your `doom!' block, or a module autoload file, is modified."
return t))) return t)))
(ignore (ignore
(print! (success "Skipping core autoloads, they are up-to-date")) (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")) (print! (start "Regenerating core autoloads file"))
(if (doom-delete-autoloads-file doom-autoload-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))) return t)))
(ignore (ignore
(print! (success "Skipping package autoloads, they are up-to-date")) (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'. (let (;; The following bindings are in `package-generate-autoloads'.
;; Presumably for a good reason, so I just copied them ;; Presumably for a good reason, so I just copied them
(noninteractive t) (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 ;; 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. ;; fully loaded. Which usually aren't so in an noninteractive session.
(doom-initialize-autoloads doom-autoload-file) (doom-load-autoloads-file doom-autoload-file)
(doom-initialize-autoloads doom-package-autoload-file) (doom-load-autoloads-file doom-package-autoload-file)
;; ;;
(unless target-dirs (unless target-dirs

View file

@ -98,7 +98,7 @@ default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in
"#\n" "#\n"
"# It is NOT safe to edit this file. Changes will be overwritten next time that\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" "# `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" "#\n"
"# To auto-regenerate this file when `doom reload` is run, use `doom env auto' or\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" "# set DOOMENV=1 in your shell environment/config.\n"

View file

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

View file

@ -144,13 +144,6 @@ BODY will be run when this dispatcher is called."
;; ;;
;;; Dispatch commands ;;; 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 ;; Load all of our subcommands
(def-command! (refresh re) (&optional force-p) (def-command! (refresh re) (&optional force-p)
"Ensure Doom is properly set up. "Ensure Doom is properly set up.

View file

@ -1,7 +1,5 @@
;;; core/core-packages.el -*- lexical-binding: t; -*- ;;; core/core-packages.el -*- lexical-binding: t; -*-
(require 'core-modules)
;; Emacs package management is opinionated, and so is Doom. Doom uses `straight' ;; Emacs package management is opinionated, and so is Doom. Doom uses `straight'
;; to create a declarative, lazy-loaded and optionally rolling-release package ;; to create a declarative, lazy-loaded and optionally rolling-release package
;; management system. We use `straight' over `package' because the latter is ;; 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 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." necessary package metadata is initialized and available for them."
(when (or force-p (not doom-init-packages-p)) (when (or force-p (not doom-init-packages-p))
(doom-log "Initializing straight")
(setq doom-init-packages-p t) (setq doom-init-packages-p t)
(straight--reset-caches) (straight--reset-caches)
(mapc #'straight-use-recipes doom-core-package-sources) (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))) (dolist (package (straight--directory-files (straight--build-dir)))
(add-to-list 'load-path (directory-file-name (straight--build-dir package))))) (add-to-list 'load-path (directory-file-name (straight--build-dir package)))))
(when (or force-p (not doom-packages)) (when (or force-p (not doom-packages))
;; On first install, the packages API will be unavailable (doom-log "Initializing doom-packages")
(unless (fboundp 'doom-package-list)
(load! "autoload/packages.el"))
(setq doom-disabled-packages nil (setq doom-disabled-packages nil
doom-packages (doom-package-list)) doom-packages (doom-package-list))
(cl-loop for (pkg . plist) in doom-packages (cl-loop for (pkg . plist) in doom-packages
for ignored = (eval (plist-get plist :ignore) t) for ignored = (eval (plist-get plist :ignore) t)
for disabled = (eval (plist-get plist :disable) t) for disabled = (eval (plist-get plist :disable) t)
if disabled if disabled
do (add-to-list 'doom-disabled-packages pkg) do (cl-pushnew pkg doom-disabled-packages)
else if (not ignored) else if (not ignored)
do (with-demoted-errors "Package error: %s" do (with-demoted-errors "Package error: %s"
(straight-register-package (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. ;; version of Emacs that doesn't match the one it was compiled with.
;; Getting this error isn't very good UX... ;; Getting this error isn't very good UX...
(catch 'emacs-version-changed (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) (unless (file-exists-p bootstrap-file)
(with-current-buffer (with-current-buffer
(url-retrieve-synchronously (url-retrieve-synchronously
@ -188,7 +187,7 @@ necessary package metadata is initialized and available for them."
(cl-return-from 'straight t)) (cl-return-from 'straight t))
;; ...so we transform it into a more graceful error message: ;; ...so we transform it into a more graceful error message:
(with-temp-buffer (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))) (let ((_ (read (current-buffer)))
(last-emacs-version (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'." (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)) 'window-setup-hook))
(run-hook-wrapped hook #'doom-try-run-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 "Tries to load FILE (an autoloads file). Return t on success, throws an error
in interactive sessions, nil otherwise (but logs a warning)." in interactive sessions, nil otherwise (but logs a warning)."
(condition-case e (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)) (message "Autoload file warning: %s -> %s" (car e) (error-message-string e))
(signal 'doom-autoload-error (list (file-name-nondirectory file) 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." "Read and set envvars in FILE."
(if (not (file-readable-p 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 (with-temp-buffer
(insert-file-contents file) (insert-file-contents file)
(search-forward "\n\n" nil t) (search-forward "\n\n" nil t)
@ -497,68 +498,77 @@ to least)."
(when (or force-p (not doom-init-p)) (when (or force-p (not doom-init-p))
(setq doom-init-p t) (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 (setq exec-path doom--initial-exec-path
load-path doom--initial-load-path load-path doom--initial-load-path
process-environment doom--initial-process-environment) process-environment doom--initial-process-environment)
(require 'core-lib) (require 'core-lib)
(require 'core-modules) (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. (if (and core-autoloads-p (not force-p))
;; This includes everything in core/autoload/*.el and autoload files in ;; In case we want to use package.el or straight via M-x
;; enabled modules. (progn
(when (or (not (doom-initialize-autoloads doom-autoload-file)) (after! (:or package straight)
force-p) (require 'core-packages))
(dolist (dir (list doom-local-dir doom-etc-dir doom-cache-dir doom-elpa-dir)) (after! straight
(unless (file-directory-p dir) (doom-initialize-packages)))
(make-directory dir 'parents-too)))
;; Ensure the package management system (and straight) are ready for ;; Eagerly load these libraries because this module may be loaded in a session
;; action (and all core packages/repos are installed) ;; that hasn't been fully initialized (where autoloads files haven't been
(require 'core-packages) ;; generated or `load-path' populated).
(doom-ensure-straight) (let ((default-directory doom-core-dir))
(doom-initialize-packages force-p) (dolist (file (file-expand-wildcards "autoload/*.el"))
(load file t t)))
(unless (or force-p noninteractive) ;; Create all our core directories to quell file errors
(user-error "Your doom autoloads are missing! Run `bin/doom refresh' to regenerate them"))) (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 ;; Ensure the package management system (and straight) are ready for
;; autoloads file which caches `load-path', `auto-mode-alist', ;; action (and all core packages/repos are installed)
;; `Info-directory-list', and `doom-disabled-packages'. A big reduction in (require 'core-packages)
;; startup time. (doom-ensure-straight)
(unless (or force-p (doom-initialize-packages force-p))
(doom-initialize-autoloads doom-package-autoload-file)
noninteractive)
(user-error "Your package autoloads are missing! Run `bin/doom refresh' to regenerate them"))
;; Load shell environment (unless (or (and core-autoloads-p pkg-autoloads-p)
(when (and (not noninteractive) force-p
(file-exists-p doom-env-file)) noninteractive)
(doom-load-env-vars doom-env-file)))) (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 ;;; Bootstrap Doom
(doom-initialize noninteractive) (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 (if noninteractive
(require 'core-cli) (require 'core-cli)
(add-hook 'window-setup-hook #'doom-display-benchmark-h) (add-hook 'window-setup-hook #'doom-display-benchmark-h)
(require 'core-keybinds) (require 'core-keybinds)
(require 'core-ui) (require 'core-ui)
(require 'core-projects) (require 'core-projects)
(require 'core-editor) (require 'core-editor))
(doom-initialize-modules))
(doom-initialize-modules)
(provide 'core) (provide 'core)
;;; core.el ends here ;;; core.el ends here