Since there is more to do on init than just loading a file and on the other hand symlinking .emacs.d
or changing HOME
changes my runtime environment, I opted for a variant of what @glucas had proposed. I used the code from startup.el
and added the patch from #15539 to use an environment variable for switching between different init dirs. If none is given, the default is used.
There was one problem with spacemacs: async
doesn't know about the changed init directory and so cannot find some necessary files. But this has been resolved in spacemacs recently: Error when using a config directory other than .emacs.d · Issue #3390
So here is my ~/.emacs
that should behave like the original init-code but with configurable init directory:
;;; .emacs --- let the user choose the emacs environment to use
;;; Commentary:
;;; This code mimics the behaviour of `startup.el' to let the
;;; usage of the custom init directory behave just like the
;;; one and only "~/.emacs.d".
;;;
;;; By setting the environment variable `EMACS_USER_DIRECTORY'
;;; the user-emacs-directory can be chosen and if there is an
;;; `init.el' the configuration from that directory will be used.
;;; If the environment variable is not set or there is no `init.el'
;;; the default configuration directory `~/.emacs.d/' will be used.
;;;
;;; The variable `server-name' will be set to the name of the directory
;;; chosen as start path. So if the server will be started, it can be
;;; reached with 'emacsclient -s servername'.
;;;
;;; This now works with a current version of spacemacs but does not
;;; work with `async-start' in general, if the code executed with `async'
;;; uses `user-init-dir' or makes other assumptions about the emacs
;;; start-directory.
;;; Code:
(let* ((user-init-dir-default
(file-name-as-directory (concat "~" init-file-user "/.emacs.d")))
(user-init-dir
(file-name-as-directory (or (getenv "EMACS_USER_DIRECTORY")
user-init-dir-default)))
(user-init-file-1
(expand-file-name "init" user-init-dir)))
(setq user-emacs-directory user-init-dir)
(with-eval-after-load "server"
(setq server-name
(let ((server--name (file-name-nondirectory
(directory-file-name user-emacs-directory))))
(if (equal server--name ".emacs.d")
"server"
server--name))))
(setq user-init-file t)
(load user-init-file-1 t t)
(when (eq user-init-file t)
(setq user-emacs-directory user-init-dir-default)
(load (expand-file-name "init" user-init-dir-default) t t)))
(provide '.emacs)
;;; .emacs ends here
There is also a nice addition that makes it work as a daemon without extra effort: the server-name will be set to the name of the init directory. So now you can start a second emacs daemon with a vanilla spacemacs
EMACS_USER_DIRECTORY=~/.emacsenv.d/spacemacs emacs --daemon
and still use emacsclient
emacsclient -s spacemacs -c -e '(message "Hello spacemacs")'
My usecase is very simple and I'm astonished, that I'm the only one: I have an always running emacs daemon and use it from gui and over console (with ssh for example). In this emacs I prepare all my documentation and work log, so it has to be there all the time. But then I want to try out spacemacs or one of the other distribution packages and even configure it, till I can retire my current configuration or use some of the clever ideas. And as others, I wanted to create a simple base configuration for my coworkers -- and document it with org-mode in my running instance.
Since the only problem I know of is async
and that it doesn't know about the changed init dir, I think about the best way to add some configuration to async
that has variables that should be injected by default, so that it's not necessary to patch all invocations of async-start
just as spacemacs had done.
spacemacs
pointing to the emacs executable and use the value of(car command-line-args)
to decide which init file to run. This is assuming, of course, that you are running on a unix system other than OS X. (The way app bundles works on OS X makes this trick difficult to carry out.)