From 6dffa09c71c67a24d776be9467600ada28ec2bc0 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Sat, 17 Sep 2022 20:21:43 +0200 Subject: [PATCH] refactor(profiles): bootstrap script - Swap out the funcall+alist lookup for a pcase (which is expanded to a cond, which is is faster and easier to read). - Wrap bootstrap file to $EMACSDIR/profiles/init.el, but byte-compile it to $EMACSDIR/profiles/init.X.el where X is emacs-major-version. - Make doom-profiles-save's second argument optional (defaults to doom-profiles-bootstrap-file). - Make doom-profiles-save throw a error if byte-compilation fails for some reason. - Rename the tempvars to include 'doom' in their name, so debuggers know where they originate. --- early-init.el | 4 +- lisp/cli/profiles.el | 4 +- lisp/doom-profiles.el | 168 +++++++++++++++++++++--------------------- 3 files changed, 89 insertions(+), 87 deletions(-) diff --git a/early-init.el b/early-init.el index 096a1740e..268085258 100644 --- a/early-init.el +++ b/early-init.el @@ -81,9 +81,9 @@ ;; $XDG_CONFIG_HOME/doom-profiles.el, and ~/.doom-profiles.el. All it ;; needs is for `$DOOMPROFILE' to be set. (setenv "DOOMPROFILE" profile) - (or (load (expand-file-name (format "profiles/init.%d" emacs-major-version) + (or (load (expand-file-name (format "profiles/init.%d.elc" emacs-major-version) user-emacs-directory) - 'noerror (not init-file-debug) nil 'must-suffix) + 'noerror (not init-file-debug) 'nosuffix) (user-error "Profiles not initialized yet; run 'doom sync' first")))) ;; PERF: When `load'ing or `require'ing files, each permutation of diff --git a/lisp/cli/profiles.el b/lisp/cli/profiles.el index 3a4c2e082..463a601e5 100644 --- a/lisp/cli/profiles.el +++ b/lisp/cli/profiles.el @@ -70,8 +70,8 @@ (dolist (p removed) (print! (item "Removed %S") (car p))) (dolist (p changed) (print! (item "Changed %S") (car p))) (doom-file-write doom-cli-known-profiles-file (list new-profiles) :mode #o600) - (doom-profiles-save new-profiles doom-profiles-bootstrap-file) - (print! (success "Regenerated profile init file: %s") + (doom-profiles-save new-profiles) + (print! (success "Regenerated profile bootstrapper: %s") (path doom-profiles-bootstrap-file))))))))) diff --git a/lisp/doom-profiles.el b/lisp/doom-profiles.el index 2ec4f11a6..418e45b5f 100644 --- a/lisp/doom-profiles.el +++ b/lisp/doom-profiles.el @@ -31,8 +31,8 @@ files, and will write a summary profiles.el to the first entry in this variable.") (defvar doom-profiles-bootstrap-file - (file-name-concat doom-emacs-dir (format "profiles/init.%d.el" emacs-major-version)) - "Where Doom writes its profile bootstrap script.") + (file-name-concat doom-emacs-dir (format "profiles/init.el" emacs-major-version)) + "Where Doom writes its interactive profile bootstrap script.") (defvar doom-profile-init-file-name (format "init.%d.el" emacs-major-version) "TODO") @@ -150,91 +150,93 @@ is non-nil, refresh the cache." ;; TODO (defun doom-profile-initialize (profile-name &optional ref) ;; ) -(defun doom-profiles-save (profiles file) +(defun doom-profiles-save (profiles &optional file) "Generate a profile bootstrapper for Doom to load at startup." + (unless file + (setq file doom-profiles-bootstrap-file)) (doom-file-write - file `(";; -*- lexical-binding: t; tab-width: 8; -*-\n" - ";; Updated: " ,(format-time-string "%Y-%m-%d %H:%M:%S") "\n" - ";; Generated by 'doom profiles sync' or 'doom sync'.\n" - ";; DO NOT EDIT THIS BY HAND!\n" - ,(format "%S" doom-version) - (funcall - (alist-get - (intern (getenv-internal "DOOMPROFILE")) - (list - ,@(cl-loop - with deferred? - = (seq-find (fn! (memq (car-safe %) '(:prepend :prepend? :append :append?))) - (mapcar #'cdr profiles)) - with deferred-varsym = (make-symbol "deferred-vars") - for (name . bindings) in profiles - collect - `(cons ',name - (lambda () - (let ,(if deferred? '(--deferred-vars--)) - ,@(cl-loop - for (var . val) in bindings - collect - (pcase (car-safe val) - (:path - `(,(if (stringp var) 'setenv 'set) - ',var ,(cl-loop with form = `(expand-file-name ,(cadr val) user-emacs-directory) - for dir in (cddr val) - do (setq form `(expand-file-name ,dir ,form)) - finally return form))) - (:eval - (if (eq var '_) - (macroexp-progn (cdr val)) - `(,(if (stringp var) 'setenv 'set) - ',var ,(macroexp-progn (cdr val))))) - (:plist - `(,(if (stringp var) 'setenv 'set) - ',var ',(if (stringp var) - (prin1-to-string (cadr val)) - (cadr val)))) - ((or :prepend :prepend?) - (if (stringp var) - `(setenv ',var (concat ,val (getenv ,var))) - (setq deferred? t) - `(push (cons ',var - (lambda () - (dolist (item (list ,@(cdr val))) - ,(if (eq (car val) :append?) - `(add-to-list ',var item) - `(push item ',var))))) - --deferred-vars--))) - ((or :append :append?) - (if (stringp var) - `(setenv ,var (concat (getenv ,var) ,val)) - (setq deferred? t) - `(push (cons ',var - (lambda () - (dolist (item (list ,@(cdr val))) - ,(if (eq (car val) :append?) - `(add-to-list ',var item 'append) - `(setq ',var (append ',var (list item))))))) - --deferred-vars--))) - (_ `(,(if (stringp var) 'setenv 'set) ',var ',val)))) - ,@(when deferred? - `((defun --defer-vars-- (_) - (dolist (var --deferred-vars--) - (when (boundp (car var)) - (funcall (cdr var)) - (setq --deferred-vars-- (delete var --deferred-vars--)))) - (unless --deferred-vars-- - (remove-hook 'after-load-functions #'--defer-vars--) - (unintern '--defer-vars-- obarray))) - (add-hook 'after-load-functions #'--defer-vars--) - (--defer-vars--)))))))) - (lambda () - (if (or noninteractive - (,(symbol-function #'doom-profiles-bootloadable-p))) - (user-error "Failed to find profile: %s" (getenv "DOOMPROFILE")) - (user-error "To be a bootloader, Doom must be installed in ~/.config/emacs or ~/.emacs.d")))))) + file (let ((profilesym (make-symbol "profile")) + (deferredsym (make-symbol "deferred-vars"))) + `(";; -*- lexical-binding: t; tab-width: 8; -*-\n" + ";; Updated: " ,(format-time-string "%Y-%m-%d %H:%M:%S") "\n" + ";; Generated by 'doom profiles sync' or 'doom sync'.\n" + ";; DO NOT EDIT THIS BY HAND!\n" + ,(format "%S" doom-version) + (pcase (intern (getenv-internal "DOOMPROFILE")) + ,@(cl-loop + for (profile-name . bindings) in profiles + for deferred? + = (seq-find (fn! (and (memq (car-safe (cdr %)) '(:prepend :prepend? :append :append?)) + (not (stringp (car-safe %))))) + bindings) + collect + `(',profile-name + (let ,(if deferred? '(--deferred-vars--)) + ,@(cl-loop + for (var . val) in bindings + collect + (pcase (car-safe val) + (:path + `(,(if (stringp var) 'setenv 'setq) + ,var ,(cl-loop with form = `(expand-file-name ,(cadr val) user-emacs-directory) + for dir in (cddr val) + do (setq form `(expand-file-name ,dir ,form)) + finally return form))) + (:eval + (if (eq var '_) + (macroexp-progn (cdr val)) + `(,(if (stringp var) 'setenv 'setq) + ,var ,(macroexp-progn (cdr val))))) + (:plist + `(,(if (stringp var) 'setenv 'setq) + ,var ',(if (stringp var) + (prin1-to-string (cadr val)) + (cadr val)))) + ((or :prepend :prepend?) + (if (stringp var) + `(setenv ,var (concat ,val (getenv ,var))) + (setq deferred? t) + `(push (cons ',var + (lambda () + (dolist (item (list ,@(cdr val))) + ,(if (eq (car val) :append?) + `(add-to-list ',var item) + `(push item ,var))))) + --deferred-vars--))) + ((or :append :append?) + (if (stringp var) + `(setenv ,var (concat (getenv ,var) ,val)) + (setq deferred? t) + `(push (cons ',var + (lambda () + (dolist (item (list ,@(cdr val))) + ,(if (eq (car val) :append?) + `(add-to-list ',var item 'append) + `(set ',var (append ,var (list item))))))) + --deferred-vars--))) + (_ `(,(if (stringp var) 'setenv 'setq) ,var ',val)))) + ,@(when deferred? + `((defun --doom-profile-set-deferred-vars-- (_) + (dolist (var --deferred-vars--) + (when (boundp (car var)) + (funcall (cdr var)) + (setq --deferred-vars-- (delete var --deferred-vars--)))) + (unless --deferred-vars-- + (remove-hook 'after-load-functions #'--doom-profile-set-deferred-vars--) + (unintern '--doom-profile-set-deferred-vars-- obarray))) + (add-hook 'after-load-functions #'--doom-profile-set-deferred-vars--) + (--doom-profile-set-deferred-vars-- nil))))))))) :mode #o600 :printfn #'pp) - (let ((byte-compile-warnings (if init-file-debug byte-compile-warnings))) - (print-group! (byte-compile-file file)))) + (print-group! + (or (let ((byte-compile-warnings (if init-file-debug byte-compile-warnings)) + (byte-compile-dest-file-function + (lambda (_) (format "%s.%d.elc" (file-name-sans-extension file) emacs-major-version)))) + (byte-compile-file file)) + ;; Do it again? So the errors/warnings are visible? + ;; (let ((byte-compile-warnings t)) + ;; (byte-compile-file file)) + (signal 'doom-profile-error (list file "Failed to byte-compile bootstrap file"))))) (defun doom-profile-p (profile-name) "Return t if PROFILE-NAME is a valid and existing profile."