32

I would like to try spacemacs. But I don't want to switch from my regular emacs configuration just yet, so I would like the configuration that's currently in my .emacs.d to be in one directory, and the equivalent configuration for spacemacs to be in another directory. I don't mind what name either directory has.

Is that possible, short of chroots / LD_PRELOAD / other such trickery? How can I do it?

  • 2
    See also Dan's answer to a similar question I had regarding setting up a timer with options when Emacs starts: emacs.stackexchange.com/questions/3588/… I have been using a slight variation of that answer ever since. – lawlist Jan 30 '16 at 5:16
  • 1
    In the category of “other trickery”, perhaps, you could create a symlink 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.) – Harald Hanche-Olsen Feb 2 '16 at 8:22
  • 1
    See another approach here: emacs.stackexchange.com/q/4253/780 – glucas Oct 28 '16 at 0:35
  • 2
    I just came back to say, about 18 months later, this is still the reason I've not learned spacemacs. I had a few niggling problems with the accepted answer then, just tried again, still true. After adding org layer got "Symbol's function definition is void: loop" just now. I don't KNOW this is part of the cause, but that's exactly the problem: For a polished "batteries included" project, it seems odd to have to cobble something together just to START it, then wonder if my configuration is supported? Perhaps unfair but that makes me lose trust. Maybe in another 18 months it'll be fixed... – Croad Langshan Jun 4 '17 at 1:02
20

One option is to specify which configuration you want to load in a ~/.emacs file. When Emacs starts up it will look for such a file before looking in ~/.emacs.d/. For more details you can read about the Init File in the Emacs manual.

So for example you might create a ~/.spacemacs.d and keep your existing ~/.emacs.d as it is. Then create a ~/.emacs to load whichever one you are in the mood to use:

(setq use-spacemacs t)   ; or nil 

(when use-spacemacs
  (setq user-emacs-directory "~/.spacemacs.d/"))   ; defaults to ~/.emacs.d/

(load (expand-file-name "init.el" user-emacs-directory))

If you want you can come up with ways to switch configs without modifying the .emacs file. For example, have your .emacs check for some environment variable that you set in your shell or in a script that you use to start spacemacs:

(setq use-spacemacs (getenv "USE_SPACEMACS"))
  • 1
    Surely, there must be a way from inside elisp to tell if you're running spacemacs or not? For example, by checking (with fboundp) for a function or command that is unique to spacemacs? – Harald Hanche-Olsen Jan 30 '16 at 20:28
  • 2
    Sure, but that wasn't the question as I understood it. Spacemacs is just an emacs config, and the OP wants to know how to maintain more than one independent emacs config dir. – glucas Jan 30 '16 at 20:31
  • 1
    @HaraldHanche-Olsen that surprised me too when I downloaded it, but when I thought about it it made total sense. I think the reason we expect that is that for most systems, to make a polished 'product' like spacemacs seems to be, it's necessary to trundle out some lower-level systems language - but in emacs there is so much extensibility (thanks elisp) that that isn't necessary. – Croad Langshan Feb 1 '16 at 23:08
  • 1
    I accepted this, but hadn't actually tried it. Now that I have, it doesn't quite work as-is: File error: Cannot open load file, /home/me/.emacs.d/core/core-load-paths.el -- I needed to (setq user-emacs-directory "~/.spacemacs.d/") for it to work (in a progn on the spacemacs branch of your conditional). Can you add that to your answer so I can accept it again? – Croad Langshan Feb 2 '16 at 0:22
  • 2
    @glucas I'm on win10, for some reason HOME was assigned to c:/users/nate . Removed the environmental variable and everything worked fine. – Nathaniel Saxe Jul 11 '17 at 16:32
12

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.

  • This is really lovely and worked beautifully... until I needed to use async. :-( Since it's been a few months since you posted, do you have a workaround? – Trey Sep 2 '16 at 15:48
  • Unfortunately not - but for spacemacs it is fixed and for general async I am not really sure wether there must be any change, because async-start starts emacs without anything and if you want to use some variable you have to pass it in. But sure, it would be nice and convenient, if async could be configured with a list of variables, that will be used on every invocation of async-start. – Uwe Koloska Sep 8 '16 at 19:08
  • 1
    With this approach, there may be other variables that get initialized from the default user-emacs-directory before your .emacs is loaded. For example I needed to add (setq custom-theme-directory user-emacs-directory) here. – glucas Oct 28 '16 at 1:34
  • 1
    I don't know where this variable is set, because when I examine it inside a minimal testenv, I get: Its value is "~/.emacs.d/". Original value was "~/.emacsenv.d/init-test/" So it has the right value, but this was overwritten. – Uwe Koloska Nov 2 '16 at 11:50
  • 1
    I actually ended up using this answer, long after I asked the question. I haven't changed the accepted answer because it's so long and because I haven't actually paid much attention to the implementation! This answer has been very useful to me, though. – Croad Langshan Nov 12 '17 at 12:09
5

It is explained here, and there is an ongoing PR to add it to the documentation:

mkdir ~/spacemacs
git clone git@github.com:syl20bnr/spacemacs.git ~/spacemacs/.emacs.d
HOME=~/spacemacs emacs```
  • 6
    Yes, I sometimes change HOME to try out an alternate config. The downside is that HOME is used for other things that you may prefer not to affect. – glucas Feb 1 '16 at 23:11
3

It's already answered and accepted, but if you feel like trying new ways of doing things the reversible way (beyond emacs configs) I'd recommend you to spend half an hour familiarizing yourself with GNU Stow. It's sort of ln -s on steroids, and there might be several approaches to use it. You can have the whole setup under one subdirectory (including offlineimap configs, emacs configs etc.) - that'd be environment based approach - or have a separate subdirectory for every application you have. Sweet schizophrenia.

E.g. emacs-related listing from my ~/Stow:

-> % ls ~/Stow | grep emacs
emacs 
emacs_from_scratch
spacemacs
hoodoo@T450s 2:46 [~]

There's one initial config I've managed to get usable, one 'I stick with defaults' config and one 'I'm gonna roll my own'. I can just turn each of these on and off and always have them all available. Each subdir can have a whole tree relative to my ~/ and it's pretty handy to mix and match.

  • I do use stow already as it happens. But how do you use it for this? You're just 'dynamically scoping' your .emacs.d by symlinking in/out the one you want to use, using stow? Do you ever run both at the same time? If so, I guess both emacses think they "own" .emacs.d. I wonder if that might cause some oddities with things that save state there, like I don't know say recentf, savehist, autosave, etc. especially when the two copies of emacs are running with radically different configurations? – Croad Langshan Jul 13 '17 at 18:38
  • No, I just switch the configurations as needed. – Roman Grazhdan Jul 14 '17 at 9:26
2

I checked out the patch that @Uwe Koloska used. It wasn't included in the main branch. I somewhat agree, I think that the problem should be resolved outside emacs using the shell and leaving emacs know nothing about it.

Maybe that feature would be more logical from a user perspective, using a flag would be less convoluted for some users.

In my use case I want to keep my Emacs installation and Spacemacs so my variation of the two solutions above works like this.

shell script

First of all the script for launching spacemacs:

#!/bin/sh

export EMACS_USER_DIR=~/spacemacs/emacs.d

exec emacs "$@"

This script is called spacemacs and is installed in the /usr/local/bin folder.

.emacs file

Then I need a .emacs file in my home folder that would treat properly the environment variable EMACS_USER_DIR.

(defvar user-custom-dir (getenv "EMACS_USER_DIR"))

(when (/= (length user-custom-dir) 0)
  (setq user-emacs-directory (file-name-as-directory user-custom-dir)))

(load (expand-file-name "init.el" user-emacs-directory))

I'm not experienced with elisp at all so I came up this, more experienced people could probably come up with something better. But hey, it works.

Eye-candy

Then why not add an icon:

icon

[Desktop Entry]
Name=Spacemacs
GenericName=Text Editor
Comment=Edit text
MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
Exec=spacemacs %F
Icon=/home/marcs/.icons/spacemacs.svg
Type=Application
Terminal=false
Categories=Development;TextEditor;
StartupWMClass=Emacs
Keywords=Text;Editor;
  • 3
    Your shell-script should use exec emacs "$@". – Uwe Koloska Oct 3 '16 at 10:56
  • Yes, thanks for the tip, just fixed. – Marcs Oct 27 '16 at 0:27
2

I find Using Chemacs really easy for switching between different emacs configs.

I made an answer here: https://emacs.stackexchange.com/a/44678/11979 .

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.