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
;; 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

View file

@ -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)))))))))

View file

@ -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."