feat: make bin/doom profile aware

- Fixes Doom's former inability to (trivially) juggle multiple profiles
  based on the same EMACSDIR (see #6593).
- Adds '--profile NAME' switch to bin/doom (also recognized
  $DOOMPROFILE).
- Adds new doom-profile* variables. These will eventually replace
  doom-{local,etc,cache}-dir and doom-{autoloads,env}-file.

This is intentionally messy to ensure backwards compatibility for a
little while longer. This will be fixed over the next couple weeks.

Ref: #6593
This commit is contained in:
Henrik Lissner 2022-07-27 22:25:08 +02:00
parent 1ecb5c7b9b
commit 5af38fb08e
No known key found for this signature in database
GPG key ID: B60957CA074D39A3
5 changed files with 154 additions and 67 deletions

View file

@ -180,8 +180,7 @@ SEE ALSO:
(doomdir ("--doomdir" dir) "Use Doom config living in `DIR' (e.g. ~/.doom.d)") (doomdir ("--doomdir" dir) "Use Doom config living in `DIR' (e.g. ~/.doom.d)")
(emacsdir ("--emacsdir" dir) "Use Doom install living in `DIR' (e.g. ~/.emacs.d)") (emacsdir ("--emacsdir" dir) "Use Doom install living in `DIR' (e.g. ~/.emacs.d)")
(pager ("--pager" cmd) "Pager command to use for large output") (pager ("--pager" cmd) "Pager command to use for large output")
;; TODO Implement after v3.0 (profile ("--profile" name) "Use profile named NAME")
;; (profile ("--profile" name) "Use profile named NAME")
&flags &flags
(color? ("--color") "Whether or not to show ANSI color codes") (color? ("--color") "Whether or not to show ANSI color codes")
&multiple &multiple
@ -205,13 +204,12 @@ SEE ALSO:
(setq doom-print-backend (if (eq color? :yes) 'ansi))) (setq doom-print-backend (if (eq color? :yes) 'ansi)))
;; For these settings to take full effect, the script must be restarted: ;; For these settings to take full effect, the script must be restarted:
(when (and (equal (doom-cli-context-step context) 0) (when (and (equal (doom-cli-context-step context) 0)
(or ;; profile (or profile
debug? debug?
emacsdir emacsdir
doomdir)) doomdir))
;; TODO Implement after v3.0 (when profile
;; (when profile (setenv "DOOMPROFILE" profile))
;; (setenv "DOOMPROFILE" profile))
(when debug? (when debug?
(setenv "DEBUG" "1") (setenv "DEBUG" "1")
(print! (item "Debug mode enabled"))) (print! (item "Debug mode enabled")))

View file

@ -49,7 +49,10 @@ OPTIONS:
(add-hook 'kill-emacs-hook #'doom-sync--abort-warning-h) (add-hook 'kill-emacs-hook #'doom-sync--abort-warning-h)
(when jobs (when jobs
(setq native-comp-async-jobs-number (truncate jobs))) (setq native-comp-async-jobs-number (truncate jobs)))
(print! (start "Synchronizing your config with Doom Emacs...")) (print! (start "Synchronizing %S profile..." )
(if doom-profile
(car (split-string doom-profile "@"))
"default"))
(unwind-protect (unwind-protect
(print-group! (print-group!
(when (and (not noenvvar?) (when (and (not noenvvar?)

View file

@ -39,6 +39,20 @@
;; Ensure errors are sufficiently detailed from this point on. ;; Ensure errors are sufficiently detailed from this point on.
(setq debug-on-error t) (setq debug-on-error t)
;;; Initialize profile
(let ((profile (getenv "DOOMPROFILE")))
(when profile
(with-temp-buffer
(let ((coding-system-for-read 'utf-8-auto))
(insert-file-contents (expand-file-name "profiles.el" user-emacs-directory)))
(condition-case e
(dolist (var (or (cdr (assq (intern profile) (read (current-buffer))))
(user-error "No %S profile found" profile)))
(if (eq var 'env)
(dolist (env var) (setenv (car env) (cdr env)))
(set (car var) (cdr var))))
(error (error "Failed to parse profiles.el: %s" (error-message-string e)))))))
;; HACK Load `cl' and site files manually to prevent polluting logs and stdout ;; HACK Load `cl' and site files manually to prevent polluting logs and stdout
;; with deprecation and/or file load messages. ;; with deprecation and/or file load messages.
(let ((inhibit-message (not (or (getenv "DEBUG") init-file-debug)))) (let ((inhibit-message (not (or (getenv "DEBUG") init-file-debug))))

View file

@ -128,30 +128,6 @@
(defconst doom-modules-dir (concat doom-emacs-dir "modules/") (defconst doom-modules-dir (concat doom-emacs-dir "modules/")
"The root directory for Doom's modules. Must end with a slash.") "The root directory for Doom's modules. Must end with a slash.")
(defconst doom-local-dir
(if-let (localdir (getenv-internal "DOOMLOCALDIR"))
(expand-file-name (file-name-as-directory localdir))
(concat doom-emacs-dir ".local/"))
"Root directory for local storage.
Use this as a storage location for this system's installation of Doom Emacs.
These files should not be shared across systems. By default, it is used by
`doom-etc-dir' and `doom-cache-dir'. Must end with a slash.")
;; DEPRECATED
(defconst doom-etc-dir (concat doom-local-dir "etc/")
"Directory for non-volatile local storage.
Use this for files that don't change much, like server binaries, external
dependencies or long-term shared data. Must end with a slash.")
;; DEPRECATED
(defconst doom-cache-dir (concat doom-local-dir "cache/")
"Directory for volatile local storage.
Use this for files that change often, like cache files. Must end with a slash.")
(defconst doom-docs-dir (concat doom-emacs-dir "docs/") (defconst doom-docs-dir (concat doom-emacs-dir "docs/")
"Where Doom's documentation files are stored. Must end with a slash.") "Where Doom's documentation files are stored. Must end with a slash.")
@ -169,15 +145,109 @@ Use this for files that change often, like cache files. Must end with a slash.")
Defaults to ~/.config/doom, ~/.doom.d or the value of the DOOMDIR envvar; Defaults to ~/.config/doom, ~/.doom.d or the value of the DOOMDIR envvar;
whichever is found first. Must end in a slash.") whichever is found first. Must end in a slash.")
;; DEPRECATED (defconst doom-profile
(if-let (profile (getenv-internal "DOOMPROFILE"))
;; DEPRECATED Use `string-search' once 27 support is dropped
(if (string-match-p "@" profile)
profile
(concat profile "@latest"))
;; TODO Restore this when profile system is complete
;; "default@latest"
)
"The name of the active profile.")
;; TODO Use me
(defconst doom-profiles-file
(expand-file-name "profiles.el" user-emacs-directory)
"TODO")
(defconst doom-profiles-dir
(if-let (profilesdir (getenv-internal "DOOMPROFILESDIR"))
(expand-file-name "./" profilesdir)
(expand-file-name "profiles/" doom-emacs-dir))
"Where Doom stores its profiles.
Profiles are essentially snapshots of Doom Emacs environments. Every time you
update or sync, you create a new generation of a profile (which can be easily
rolled back or switched between with the DOOMPROFILE envvar). Must end in a
slash.")
(defconst doom-profile-dir
(expand-file-name (concat (or doom-profile "default@latest") "/")
doom-profiles-dir)
"The path to the current, active profile.
Must end in a slash.")
(defconst doom-profile-data-dir
(expand-file-name "data/" doom-profile-dir)
"Where file storage/servers for the current, active profile is kept.
Use this for long-living files that contain shared data that the user would
reasonably want to keep, and/or are required for Emacs to function correctly.
Must end in a slash.")
(defconst doom-profile-cache-dir
(expand-file-name "cache/" doom-profile-dir)
"Where file caches for the current, active profile is kept.
Use this for non-essential data files that, when deleted, won't cause breakage
or misbehavior, and can be restored. This includes server binaries or programs
downloaded/installed by packages. Must end in a slash.")
(defconst doom-profile-init-file
(expand-file-name "init.el" doom-profile-dir)
"TODO")
;;
;;; DEPRECATED file/directory vars
(defconst doom-local-dir
(if-let (localdir (getenv-internal "DOOMLOCALDIR"))
(expand-file-name (file-name-as-directory localdir))
(if doom-profile
(expand-file-name doom-profile doom-profiles-dir)
(format "%s.local%s/"
doom-emacs-dir
(if doom-profile (concat "." doom-profile) ""))))
"Root directory for local storage.
Use this as a storage location for this system's installation of Doom Emacs.
These files should not be shared across systems. By default, it is used by
`doom-etc-dir' and `doom-cache-dir'. Must end with a slash.")
(defconst doom-etc-dir
(if doom-profile
doom-profile-data-dir
(concat doom-local-dir "etc/"))
"Directory for non-volatile local storage.
Use this for files that don't change much, like server binaries, external
dependencies or long-term shared data. Must end with a slash.")
(defconst doom-cache-dir
(if doom-profile
doom-profile-cache-dir
(concat doom-local-dir "cache/"))
"Directory for volatile local storage.
Use this for files that change often, like cache files. Must end with a slash.")
(defconst doom-autoloads-file (defconst doom-autoloads-file
(concat doom-local-dir "autoloads." emacs-version ".el") (if doom-profile
doom-profile-init-file
(concat doom-local-dir "autoloads." emacs-version ".el"))
"Where `doom-reload-core-autoloads' stores its core autoloads. "Where `doom-reload-core-autoloads' stores its core autoloads.
This file is responsible for informing Emacs where to find all of Doom's This file is responsible for informing Emacs where to find all of Doom's
autoloaded core functions (in core/autoload/*.el).") autoloaded core functions (in core/autoload/*.el).")
(defconst doom-env-file (concat doom-local-dir "env") (defconst doom-env-file
(if doom-profile
(expand-file-name "env" doom-profile-dir)
(concat doom-local-dir "env"))
"The location of your envvar file, generated by `doom env`. "The location of your envvar file, generated by `doom env`.
This file contains environment variables scraped from your shell environment, This file contains environment variables scraped from your shell environment,

View file

@ -121,44 +121,46 @@
(setq user-emacs-directory profile-dir) (setq user-emacs-directory profile-dir)
(throw 'found t))) (throw 'found t)))
(user-error "No %S profile found" profile)))))) (user-error "No %S profile found" profile)))
;; Ensure the selected profile persists through the session
(setenv "DOOMPROFILE" profile))))
;; ;;
;;; Bootstrap ;;; Bootstrap
;; Let er rip ;; Load the heart of Doom Emacs
(let (init-file) (unless (load (expand-file-name "core/core" user-emacs-directory) t t)
;; Load the heart of Doom Emacs ;; ...but if that fails, then this is likely not a Doom config.
(if (load (expand-file-name "core/core" user-emacs-directory) t t) (setq early-init-file (expand-file-name "early-init" user-emacs-directory))
;; ...and prepare it for an interactive session. (load early-init-file t t))
(setq init-file (expand-file-name "core-start" doom-core-dir))
;; ...but if that fails, then this is likely not a Doom config.
(setq early-init-file (expand-file-name "early-init" user-emacs-directory))
(load early-init-file t t))
;; We hijack Emacs' initfile resolver to inject our own entry point. Why do ;; We hijack Emacs' initfile resolver to inject our own entry point. Why do
;; this? Because: ;; this? Because:
;; ;;
;; - It spares Emacs the effort of looking for/loading useless initfiles, like ;; - It spares Emacs the effort of looking for/loading useless initfiles, like
;; ~/.emacs and ~/_emacs. And skips ~/.emacs.d/init.el, which won't exist if ;; ~/.emacs and ~/_emacs. And skips ~/.emacs.d/init.el, which won't exist if
;; you're using Doom (fyi: doom hackers or chemacs users could then use ;; you're using Doom (fyi: doom hackers or chemacs users could then use
;; $EMACSDIR as their $DOOMDIR, if they wanted). ;; $EMACSDIR as their $DOOMDIR, if they wanted).
;; - Later, 'doom sync' will dynamically generate its bootstrap file, which ;; - Later, 'doom sync' will dynamically generate its bootstrap file, which
;; will be important for Doom's profile system later. Until then, we'll use ;; will be important for Doom's profile system later. Until then, we'll use
;; core/core-start.el. ;; core/core-start.el.
;; - A "fallback" initfile can be trivially specified, in case the ;; - A "fallback" initfile can be trivially specified, in case the
;; bootstrapper is missing (if the user hasn't run 'doom sync' or is a ;; bootstrapper is missing (if the user hasn't run 'doom sync' or is a
;; first-timer). This is an opportunity to display a "safe mode" environment ;; first-timer). This is an opportunity to display a "safe mode" environment
;; that's less intimidating and more helpful than the broken state errors ;; that's less intimidating and more helpful than the broken state errors
;; would've left Emacs in, otherwise. ;; would've left Emacs in, otherwise.
;; - A generated config allows for a file IO optimized startup. ;; - A generated config allows for a file IO optimized startup.
(define-advice startup--load-user-init-file (:filter-args (args) init-doom) (define-advice startup--load-user-init-file (:filter-args (args) init-doom)
"Initialize Doom Emacs in an interactive session." "Initialize Doom Emacs in an interactive session."
(list (lambda () (list (lambda ()
(or init-file (if (boundp 'doom-core-dir)
(expand-file-name "init.el" user-emacs-directory))) (expand-file-name "core-start" doom-core-dir)
nil ; TODO Replace with safe mode initfile (expand-file-name "init.el" user-emacs-directory)))
(caddr args)))) (when (boundp 'doom-profiles-dir)
(lambda ()
(expand-file-name "safe-mode@static/init.el" doom-profiles-dir)))
(caddr args)))
;;; early-init.el ends here ;;; early-init.el ends here