From ab07e073522e859f2a14edff18a0f0cd29bf51b5 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Mon, 18 Jun 2018 14:47:36 +0200 Subject: [PATCH] Improve general error handling at startup This will hopefully reveal more information as to the cause and origin of errors at startup. It should also make doom-debug-mode more likely to produce a backtrace in non-interactive sessions. --- core/autoload/help.el | 2 +- core/core-dispatcher.el | 7 +--- core/core-modules.el | 25 +++++++---- core/core-packages.el | 91 +++++++++++++++++++---------------------- core/core-ui.el | 2 +- core/core.el | 28 +++++++++++-- 6 files changed, 87 insertions(+), 68 deletions(-) diff --git a/core/autoload/help.el b/core/autoload/help.el index ec5aa41c3..3eae63701 100644 --- a/core/autoload/help.el +++ b/core/autoload/help.el @@ -251,5 +251,5 @@ init.el and config.el. Then runs `doom-reload-hook'." (with-demoted-errors "PRIVATE CONFIG ERROR: %s" (doom-initialize-modules 'force)) (print! (green "%d packages reloaded" (length package-alist))) - (run-hooks 'doom-reload-hook) + (run-hook-wrapped 'doom-reload-hook #'doom-try-run-hook) t)))) diff --git a/core/core-dispatcher.el b/core/core-dispatcher.el index 06d8a1529..491d39bc7 100644 --- a/core/core-dispatcher.el +++ b/core/core-dispatcher.el @@ -770,12 +770,9 @@ If RECOMPILE-P is non-nil, only recompile out-of-date files." total-ok (- (length target-files) total-noop) total-noop)))) (error - (print! (red "\n%%s\n\n%%s\n\n%%s") - "There were breaking errors." - (error-message-string ex) + (print! (red "\n%s\n\n%%s" "There were breaking errors.") "Reverting changes...") - (quiet! (doom-clean-byte-compiled-files)) - (print! (yellow "Finished (nothing was byte-compiled)")))))))) + (signal 'doom-error (list 'byte-compile e)))))))) (defun doom-clean-byte-compiled-files () "Delete all the compiled elc files in your Emacs configuration and private diff --git a/core/core-modules.el b/core/core-modules.el index 714feb350..1e2cd6929 100644 --- a/core/core-modules.el +++ b/core/core-modules.el @@ -27,6 +27,10 @@ A warning will be put out if these deprecated modules are used.") ;; Bootstrap API ;; +;; Custom errors +(define-error 'doom-autoload-error "Error in your autoloads file(s)" 'doom-error) +(define-error 'doom-private-error "Error in your private config" 'doom-error) + (defun doom-initialize-modules (&optional force-p) "Loads the init.el in `doom-private-dir' and sets up hooks for a healthy session of Dooming. Will noop if used more than once, unless FORCE-P is @@ -36,16 +40,19 @@ non-nil." ;; recurse by accident if any of them need `doom-initialize-modules'. (setq doom-init-modules-p t) (when doom-private-dir - (load (expand-file-name "init" doom-private-dir) - 'noerror 'nomessage)))) + (condition-case e + (load (expand-file-name "init" doom-private-dir) + 'noerror 'nomessage) + (error (signal 'doom-private-error (list 'init e))))))) (defun doom-initialize-autoloads (file) "Tries to load FILE (an autoloads file). Return t on success, nil otherwise." - (condition-case-unless-debug e + (condition-case e (load (file-name-sans-extension file) 'noerror 'nomessage) ('error - (message "Autoload error: %s -> %s" - (car e) (error-message-string e))))) + (if noninteractive + (message "Autoload file warning: %s -> %s" (car e) (error-message-string e)) + (signal 'doom-autoload-error e))))) ;; @@ -289,13 +296,15 @@ to least)." `(let (file-name-handler-alist) (setq doom-modules ',doom-modules) ,@(nreverse init-forms) - (run-hooks 'doom-init-hook) + (run-hook-wrapped 'doom-init-hook #'doom-try-run-hook) (unless noninteractive (let ((doom--stage 'config)) ,@(nreverse config-forms) (when doom-private-dir - (load ,(expand-file-name "config" doom-private-dir) - t (not doom-debug-mode)))))))) + (condition-case e + (load ,(expand-file-name "config" doom-private-dir) + t (not doom-debug-mode)) + (error (signal 'doom-private-error (list 'config e)))))))))) (defvar doom-disabled-packages) (defmacro def-package! (name &rest plist) diff --git a/core/core-packages.el b/core/core-packages.el index 2933791eb..780cad089 100644 --- a/core/core-packages.el +++ b/core/core-packages.el @@ -110,31 +110,20 @@ them." (error "Could not initialize quelpa")))) (when (or force-p (not doom-packages)) - (let ((doom-modules (doom-modules))) + (let ((doom-modules (doom-modules)) + (doom--stage 'packages) + (noninteractive t)) (setq doom-packages nil) - (cl-flet - ((_load - (file &optional noerror) - (condition-case-unless-debug ex - (load file noerror 'nomessage 'nosuffix) - ('error - (lwarn 'doom-initialize-packages :warning - "%s in %s: %s" - (car ex) - (file-relative-name file doom-emacs-dir) - (error-message-string ex)))))) - (let ((doom--stage 'packages) - (noninteractive t)) - (_load (expand-file-name "packages.el" doom-core-dir)) - ;; We load the private packages file twice to ensure disabled - ;; packages are seen ASAP, and a second time to ensure privately - ;; overridden packages are properly overwritten. - (let ((private-packages (expand-file-name "packages.el" doom-private-dir))) - (_load private-packages t) - (cl-loop for key being the hash-keys of doom-modules - for path = (doom-module-path (car key) (cdr key) "packages.el") - do (let ((doom--current-module key)) (_load path t))) - (_load private-packages t))))))))) + (load (expand-file-name "packages.el" doom-core-dir) t t) + ;; We load the private packages file twice to ensure disabled + ;; packages are seen ASAP, and a second time to ensure privately + ;; overridden packages are properly overwritten. + (let ((private-packages (expand-file-name "packages.el" doom-private-dir))) + (load private-packages t t) + (cl-loop for key being the hash-keys of doom-modules + for path = (doom-module-path (car key) (cdr key) "packages.el") + do (let ((doom--current-module key)) (load path t t))) + (load private-packages t t))))))) ;; @@ -202,31 +191,35 @@ Returns t if package is successfully registered, and nil if it was disabled elsewhere." (declare (indent defun)) (doom--assert-stage-p 'packages #'package!) - (let* ((old-plist (cdr (assq name doom-packages))) - (pkg-recipe (or (plist-get plist :recipe) - (and old-plist (plist-get old-plist :recipe)))) - (pkg-pin (or (plist-get plist :pin) - (and old-plist (plist-get old-plist :pin)))) - (pkg-disable (or (plist-get plist :disable) - (and old-plist (plist-get old-plist :disable))))) - (when pkg-disable - (add-to-list 'doom-disabled-packages name nil #'eq)) - (when pkg-recipe - (when (= 0 (% (length pkg-recipe) 2)) - (setq plist (plist-put plist :recipe (cons name pkg-recipe)))) - (when pkg-pin - (setq plist (plist-put plist :pin nil)))) - (dolist (prop '(:ignore :freeze)) - (when-let* ((val (plist-get plist prop))) - (setq plist (plist-put plist prop (eval val))))) - (when (file-in-directory-p (or (bound-and-true-p byte-compile-current-file) - load-file-name) - doom-private-dir) - (setq plist (plist-put plist :private t))) - `(progn - ,(if pkg-pin `(map-put package-pinned-packages ',name ,pkg-pin)) - (map-put doom-packages ',name ',plist) - (not (memq ',name doom-disabled-packages))))) + (condition-case e + (let* ((old-plist (cdr (assq name doom-packages))) + (pkg-recipe (or (plist-get plist :recipe) + (and old-plist (plist-get old-plist :recipe)))) + (pkg-pin (or (plist-get plist :pin) + (and old-plist (plist-get old-plist :pin)))) + (pkg-disable (or (plist-get plist :disable) + (and old-plist (plist-get old-plist :disable))))) + (when pkg-disable + (add-to-list 'doom-disabled-packages name nil #'eq)) + (when pkg-recipe + (when (= 0 (% (length pkg-recipe) 2)) + (setq plist (plist-put plist :recipe (cons name pkg-recipe)))) + (when pkg-pin + (setq plist (plist-put plist :pin nil)))) + (dolist (prop '(:ignore :freeze)) + (when-let* ((val (plist-get plist prop))) + (setq plist (plist-put plist prop (eval val))))) + (when (file-in-directory-p (or (bound-and-true-p byte-compile-current-file) + load-file-name) + doom-private-dir) + (setq plist (plist-put plist :private t))) + `(progn + ,(if pkg-pin `(map-put package-pinned-packages ',name ,pkg-pin)) + (map-put doom-packages ',name ',plist) + (not (memq ',name doom-disabled-packages)))) + (error + (signal 'doom-private-error + (list (list 'packages name) e))))) (defmacro packages! (&rest packages) "A convenience macro like `package!', but allows you to declare multiple diff --git a/core/core-ui.el b/core/core-ui.el index d2d8147d2..2d8d8821d 100644 --- a/core/core-ui.el +++ b/core/core-ui.el @@ -724,7 +724,7 @@ windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original ;; Ensure ansi codes in compilation buffers are replaced (add-hook 'compilation-filter-hook #'doom|compilation-ansi-color-apply) ;; - (run-hooks 'doom-init-ui-hook)) + (run-hook-wrapped 'doom-init-ui-hook #'doom-try-run-hook)) (add-hook 'doom-post-init-hook #'doom|init-ui) diff --git a/core/core.el b/core/core.el index 60db26474..941bc0bcc 100644 --- a/core/core.el +++ b/core/core.el @@ -115,6 +115,14 @@ else (except for `window-setup-hook').") (defvar doom--stage 'init) +;; +;; Custom error types +;; + +(define-error 'doom-error "Doom Emacs error") +(define-error 'doom-hook-error "Error in a Doom startup hook" 'doom-error) + + ;; ;; Emacs core configuration ;; @@ -231,6 +239,17 @@ original value of `symbol-file'." ;; Bootstrap helpers ;; +(defun doom-try-run-hook (hook) + "Run HOOK (a hook function), but marks thrown errors to make it a little +easier to tell where the came from. + +Meant to be used with `run-hook-wrapped'." + (condition-case e + (funcall hook) + (error (signal 'doom-hook-error (list hook e)))) + ;; return nil so `run-hook-wrapped' won't short circuit + nil) + (defun doom-ensure-same-emacs-version-p () "Check if the running version of Emacs has changed and set `doom-emacs-changed-p' if it has." @@ -274,16 +293,17 @@ If RETURN-P, return the message as a string instead of displaying it." (defun doom|post-init () "Run `doom-post-init-hook'. That's all." - (run-hooks 'doom-post-init-hook)) + (run-hook-wrapped 'doom-post-init-hook #'doom-try-run-hook)) (defun doom|run-all-startup-hooks () "Run all startup Emacs hooks. Meant to be executed after starting Emacs with -q or -Q, for example: emacs -Q -l init.el -f doom|run-all-startup-hooks" - (run-hooks 'after-init-hook 'delayed-warnings-hook - 'emacs-startup-hook 'term-setup-hook - 'window-setup-hook)) + (mapc #'doom-try-run-hook + (list 'after-init-hook 'delayed-warnings-hook + 'emacs-startup-hook 'term-setup-hook + 'window-setup-hook))) ;;