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,91 +150,93 @@ 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"))
";; Updated: " ,(format-time-string "%Y-%m-%d %H:%M:%S") "\n" (deferredsym (make-symbol "deferred-vars")))
";; Generated by 'doom profiles sync' or 'doom sync'.\n" `(";; -*- lexical-binding: t; tab-width: 8; -*-\n"
";; DO NOT EDIT THIS BY HAND!\n" ";; Updated: " ,(format-time-string "%Y-%m-%d %H:%M:%S") "\n"
,(format "%S" doom-version) ";; Generated by 'doom profiles sync' or 'doom sync'.\n"
(funcall ";; DO NOT EDIT THIS BY HAND!\n"
(alist-get ,(format "%S" doom-version)
(intern (getenv-internal "DOOMPROFILE")) (pcase (intern (getenv-internal "DOOMPROFILE"))
(list ,@(cl-loop
,@(cl-loop for (profile-name . bindings) in profiles
with deferred? for deferred?
= (seq-find (fn! (memq (car-safe %) '(:prepend :prepend? :append :append?))) = (seq-find (fn! (and (memq (car-safe (cdr %)) '(:prepend :prepend? :append :append?))
(mapcar #'cdr profiles)) (not (stringp (car-safe %)))))
with deferred-varsym = (make-symbol "deferred-vars") bindings)
for (name . bindings) in profiles collect
collect `(',profile-name
`(cons ',name (let ,(if deferred? '(--deferred-vars--))
(lambda () ,@(cl-loop
(let ,(if deferred? '(--deferred-vars--)) for (var . val) in bindings
,@(cl-loop collect
for (var . val) in bindings (pcase (car-safe val)
collect (:path
(pcase (car-safe val) `(,(if (stringp var) 'setenv 'setq)
(:path ,var ,(cl-loop with form = `(expand-file-name ,(cadr val) user-emacs-directory)
`(,(if (stringp var) 'setenv 'set) for dir in (cddr val)
',var ,(cl-loop with form = `(expand-file-name ,(cadr val) user-emacs-directory) do (setq form `(expand-file-name ,dir ,form))
for dir in (cddr val) finally return form)))
do (setq form `(expand-file-name ,dir ,form)) (:eval
finally return form))) (if (eq var '_)
(:eval (macroexp-progn (cdr val))
(if (eq var '_) `(,(if (stringp var) 'setenv 'setq)
(macroexp-progn (cdr val)) ,var ,(macroexp-progn (cdr val)))))
`(,(if (stringp var) 'setenv 'set) (:plist
',var ,(macroexp-progn (cdr val))))) `(,(if (stringp var) 'setenv 'setq)
(:plist ,var ',(if (stringp var)
`(,(if (stringp var) 'setenv 'set) (prin1-to-string (cadr val))
',var ',(if (stringp var) (cadr val))))
(prin1-to-string (cadr val)) ((or :prepend :prepend?)
(cadr val)))) (if (stringp var)
((or :prepend :prepend?) `(setenv ,var (concat ,val (getenv ,var)))
(if (stringp var) (setq deferred? t)
`(setenv ',var (concat ,val (getenv ,var))) `(push (cons ',var
(setq deferred? t) (lambda ()
`(push (cons ',var (dolist (item (list ,@(cdr val)))
(lambda () ,(if (eq (car val) :append?)
(dolist (item (list ,@(cdr val))) `(add-to-list ',var item)
,(if (eq (car val) :append?) `(push item ,var)))))
`(add-to-list ',var item) --deferred-vars--)))
`(push item ',var))))) ((or :append :append?)
--deferred-vars--))) (if (stringp var)
((or :append :append?) `(setenv ,var (concat (getenv ,var) ,val))
(if (stringp var) (setq deferred? t)
`(setenv ,var (concat (getenv ,var) ,val)) `(push (cons ',var
(setq deferred? t) (lambda ()
`(push (cons ',var (dolist (item (list ,@(cdr val)))
(lambda () ,(if (eq (car val) :append?)
(dolist (item (list ,@(cdr val))) `(add-to-list ',var item 'append)
,(if (eq (car val) :append?) `(set ',var (append ,var (list item)))))))
`(add-to-list ',var item 'append) --deferred-vars--)))
`(setq ',var (append ',var (list item))))))) (_ `(,(if (stringp var) 'setenv 'setq) ,var ',val))))
--deferred-vars--))) ,@(when deferred?
(_ `(,(if (stringp var) 'setenv 'set) ',var ',val)))) `((defun --doom-profile-set-deferred-vars-- (_)
,@(when deferred? (dolist (var --deferred-vars--)
`((defun --defer-vars-- (_) (when (boundp (car var))
(dolist (var --deferred-vars--) (funcall (cdr var))
(when (boundp (car var)) (setq --deferred-vars-- (delete var --deferred-vars--))))
(funcall (cdr var)) (unless --deferred-vars--
(setq --deferred-vars-- (delete var --deferred-vars--)))) (remove-hook 'after-load-functions #'--doom-profile-set-deferred-vars--)
(unless --deferred-vars-- (unintern '--doom-profile-set-deferred-vars-- obarray)))
(remove-hook 'after-load-functions #'--defer-vars--) (add-hook 'after-load-functions #'--doom-profile-set-deferred-vars--)
(unintern '--defer-vars-- obarray))) (--doom-profile-set-deferred-vars-- nil)))))))))
(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"))))))
: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."