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.
This commit is contained in:
Henrik Lissner 2022-09-17 20:21:43 +02:00
parent 09d24cd68a
commit 6dffa09c71
No known key found for this signature in database
GPG key ID: B60957CA074D39A3
3 changed files with 89 additions and 87 deletions

View file

@ -81,9 +81,9 @@
;; $XDG_CONFIG_HOME/doom-profiles.el, and ~/.doom-profiles.el. All it ;; $XDG_CONFIG_HOME/doom-profiles.el, and ~/.doom-profiles.el. All it
;; needs is for `$DOOMPROFILE' to be set. ;; needs is for `$DOOMPROFILE' to be set.
(setenv "DOOMPROFILE" profile) (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) 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")))) (user-error "Profiles not initialized yet; run 'doom sync' first"))))
;; PERF: When `load'ing or `require'ing files, each permutation of ;; PERF: When `load'ing or `require'ing files, each permutation of

View file

@ -70,8 +70,8 @@
(dolist (p removed) (print! (item "Removed %S") (car p))) (dolist (p removed) (print! (item "Removed %S") (car p)))
(dolist (p changed) (print! (item "Changed %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-file-write doom-cli-known-profiles-file (list new-profiles) :mode #o600)
(doom-profiles-save new-profiles doom-profiles-bootstrap-file) (doom-profiles-save new-profiles)
(print! (success "Regenerated profile init file: %s") (print! (success "Regenerated profile bootstrapper: %s")
(path doom-profiles-bootstrap-file))))))))) (path doom-profiles-bootstrap-file)))))))))

View file

@ -31,8 +31,8 @@ files, and will write a summary profiles.el to the first entry in this
variable.") variable.")
(defvar doom-profiles-bootstrap-file (defvar doom-profiles-bootstrap-file
(file-name-concat doom-emacs-dir (format "profiles/init.%d.el" emacs-major-version)) (file-name-concat doom-emacs-dir (format "profiles/init.el" emacs-major-version))
"Where Doom writes its profile bootstrap script.") "Where Doom writes its interactive profile bootstrap script.")
(defvar doom-profile-init-file-name (format "init.%d.el" emacs-major-version) (defvar doom-profile-init-file-name (format "init.%d.el" emacs-major-version)
"TODO") "TODO")
@ -150,58 +150,58 @@ is non-nil, refresh the cache."
;; TODO (defun doom-profile-initialize (profile-name &optional ref) ;; 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." "Generate a profile bootstrapper for Doom to load at startup."
(unless file
(setq file doom-profiles-bootstrap-file))
(doom-file-write (doom-file-write
file `(";; -*- lexical-binding: t; tab-width: 8; -*-\n" 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" ";; Updated: " ,(format-time-string "%Y-%m-%d %H:%M:%S") "\n"
";; Generated by 'doom profiles sync' or 'doom sync'.\n" ";; Generated by 'doom profiles sync' or 'doom sync'.\n"
";; DO NOT EDIT THIS BY HAND!\n" ";; DO NOT EDIT THIS BY HAND!\n"
,(format "%S" doom-version) ,(format "%S" doom-version)
(funcall (pcase (intern (getenv-internal "DOOMPROFILE"))
(alist-get
(intern (getenv-internal "DOOMPROFILE"))
(list
,@(cl-loop ,@(cl-loop
with deferred? for (profile-name . bindings) in profiles
= (seq-find (fn! (memq (car-safe %) '(:prepend :prepend? :append :append?))) for deferred?
(mapcar #'cdr profiles)) = (seq-find (fn! (and (memq (car-safe (cdr %)) '(:prepend :prepend? :append :append?))
with deferred-varsym = (make-symbol "deferred-vars") (not (stringp (car-safe %)))))
for (name . bindings) in profiles bindings)
collect collect
`(cons ',name `(',profile-name
(lambda ()
(let ,(if deferred? '(--deferred-vars--)) (let ,(if deferred? '(--deferred-vars--))
,@(cl-loop ,@(cl-loop
for (var . val) in bindings for (var . val) in bindings
collect collect
(pcase (car-safe val) (pcase (car-safe val)
(:path (:path
`(,(if (stringp var) 'setenv 'set) `(,(if (stringp var) 'setenv 'setq)
',var ,(cl-loop with form = `(expand-file-name ,(cadr val) user-emacs-directory) ,var ,(cl-loop with form = `(expand-file-name ,(cadr val) user-emacs-directory)
for dir in (cddr val) for dir in (cddr val)
do (setq form `(expand-file-name ,dir ,form)) do (setq form `(expand-file-name ,dir ,form))
finally return form))) finally return form)))
(:eval (:eval
(if (eq var '_) (if (eq var '_)
(macroexp-progn (cdr val)) (macroexp-progn (cdr val))
`(,(if (stringp var) 'setenv 'set) `(,(if (stringp var) 'setenv 'setq)
',var ,(macroexp-progn (cdr val))))) ,var ,(macroexp-progn (cdr val)))))
(:plist (:plist
`(,(if (stringp var) 'setenv 'set) `(,(if (stringp var) 'setenv 'setq)
',var ',(if (stringp var) ,var ',(if (stringp var)
(prin1-to-string (cadr val)) (prin1-to-string (cadr val))
(cadr val)))) (cadr val))))
((or :prepend :prepend?) ((or :prepend :prepend?)
(if (stringp var) (if (stringp var)
`(setenv ',var (concat ,val (getenv ,var))) `(setenv ,var (concat ,val (getenv ,var)))
(setq deferred? t) (setq deferred? t)
`(push (cons ',var `(push (cons ',var
(lambda () (lambda ()
(dolist (item (list ,@(cdr val))) (dolist (item (list ,@(cdr val)))
,(if (eq (car val) :append?) ,(if (eq (car val) :append?)
`(add-to-list ',var item) `(add-to-list ',var item)
`(push item ',var))))) `(push item ,var)))))
--deferred-vars--))) --deferred-vars--)))
((or :append :append?) ((or :append :append?)
(if (stringp var) (if (stringp var)
@ -212,29 +212,31 @@ is non-nil, refresh the cache."
(dolist (item (list ,@(cdr val))) (dolist (item (list ,@(cdr val)))
,(if (eq (car val) :append?) ,(if (eq (car val) :append?)
`(add-to-list ',var item 'append) `(add-to-list ',var item 'append)
`(setq ',var (append ',var (list item))))))) `(set ',var (append ,var (list item)))))))
--deferred-vars--))) --deferred-vars--)))
(_ `(,(if (stringp var) 'setenv 'set) ',var ',val)))) (_ `(,(if (stringp var) 'setenv 'setq) ,var ',val))))
,@(when deferred? ,@(when deferred?
`((defun --defer-vars-- (_) `((defun --doom-profile-set-deferred-vars-- (_)
(dolist (var --deferred-vars--) (dolist (var --deferred-vars--)
(when (boundp (car var)) (when (boundp (car var))
(funcall (cdr var)) (funcall (cdr var))
(setq --deferred-vars-- (delete var --deferred-vars--)))) (setq --deferred-vars-- (delete var --deferred-vars--))))
(unless --deferred-vars-- (unless --deferred-vars--
(remove-hook 'after-load-functions #'--defer-vars--) (remove-hook 'after-load-functions #'--doom-profile-set-deferred-vars--)
(unintern '--defer-vars-- obarray))) (unintern '--doom-profile-set-deferred-vars-- obarray)))
(add-hook 'after-load-functions #'--defer-vars--) (add-hook 'after-load-functions #'--doom-profile-set-deferred-vars--)
(--defer-vars--)))))))) (--doom-profile-set-deferred-vars-- nil)))))))))
(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"))))))
:mode #o600 :mode #o600
:printfn #'pp) :printfn #'pp)
(let ((byte-compile-warnings (if init-file-debug byte-compile-warnings))) (print-group!
(print-group! (byte-compile-file file)))) (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) (defun doom-profile-p (profile-name)
"Return t if PROFILE-NAME is a valid and existing profile." "Return t if PROFILE-NAME is a valid and existing profile."