From 93f7520c79314cee82126f2d305a357469d02150 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Mon, 22 Jul 2019 22:28:43 +0200 Subject: [PATCH] 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. --- core/autoload/config.el | 6 +-- core/autoload/packages.el | 3 -- core/autoload/plist.el | 71 +++++++++++++++-------------- core/autoload/projects.el | 6 +-- core/cli/autoloads.el | 4 +- core/cli/byte-compile.el | 4 +- core/cli/env.el | 2 +- core/cli/test.el | 4 +- core/core-cli.el | 7 --- core/core-packages.el | 15 +++---- core/core.el | 94 ++++++++++++++++++++++----------------- 11 files changed, 109 insertions(+), 107 deletions(-) diff --git a/core/autoload/config.el b/core/autoload/config.el index 1a7cca105..253daa48c 100644 --- a/core/autoload/config.el +++ b/core/autoload/config.el @@ -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 () diff --git a/core/autoload/packages.el b/core/autoload/packages.el index 2462e8c55..d431500ad 100644 --- a/core/autoload/packages.el +++ b/core/autoload/packages.el @@ -1,8 +1,5 @@ ;;; core/autoload/packages.el -*- lexical-binding: t; -*- -(require 'straight) - - ;; ;;; Package metadata diff --git a/core/autoload/plist.el b/core/autoload/plist.el index 88c50b54a..c61824ecb 100644 --- a/core/autoload/plist.el +++ b/core/autoload/plist.el @@ -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)) diff --git a/core/autoload/projects.el b/core/autoload/projects.el index 6d68c2c46..f3553bbff 100644 --- a/core/autoload/projects.el +++ b/core/autoload/projects.el @@ -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))))) diff --git a/core/cli/autoloads.el b/core/cli/autoloads.el index 77309c727..edc96143b 100644 --- a/core/cli/autoloads.el +++ b/core/cli/autoloads.el @@ -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) diff --git a/core/cli/byte-compile.el b/core/cli/byte-compile.el index f08e7f1d4..cc79fbdc6 100644 --- a/core/cli/byte-compile.el +++ b/core/cli/byte-compile.el @@ -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 diff --git a/core/cli/env.el b/core/cli/env.el index f8a0cf85a..a27a9af6c 100644 --- a/core/cli/env.el +++ b/core/cli/env.el @@ -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" diff --git a/core/cli/test.el b/core/cli/test.el index eb0e79709..2f2b5a3d7 100644 --- a/core/cli/test.el +++ b/core/cli/test.el @@ -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")) diff --git a/core/core-cli.el b/core/core-cli.el index 9ef4d8af0..5cad328c3 100644 --- a/core/core-cli.el +++ b/core/core-cli.el @@ -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. diff --git a/core/core-packages.el b/core/core-packages.el index b531da351..ed7641f79 100644 --- a/core/core-packages.el +++ b/core/core-packages.el @@ -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'." diff --git a/core/core.el b/core/core.el index aa03e9381..a9995c508 100644 --- a/core/core.el +++ b/core/core.el @@ -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