76

I'm working on a custom and small Emacs configuration that I want to share with some friends as a git repository for them to use as a baseline for their own future configurations.

For this I need some way to test my configuration and the simplest solution I can come up with is something like:

$ emacs --eval "(setq user-emacs-directory \"~/Code/my_custom_emacs.d/\")"

But I can't seem to make it work.

Any help much appreciated.

Mattias Bengtsson
  • 1,300
  • 1
  • 11
  • 18

14 Answers14

53

The way I use to maintain several .emacs.d directories in parallel is the following.

  1. emacs is started like this:

    alias emacs='emacs -q --load "/path/to/init.el"'
    
  2. Each init.el file begins like this, to correctly set up the user-init-file and user-emacs-directory variables:

    (setq user-init-file (or load-file-name (buffer-file-name)))
    (setq user-emacs-directory (file-name-directory user-init-file))
    

I have found this to work very reliably over the past months. Here are a few remarks:

  • it breaks emacs-init-time, which only reports the time needed to load the default system configuration, but not your own init file. If you're interested in benchmarking your init time, you'll have to do it another way (see for example How do I measure performance of elisp code?).

  • it is not equivalent to a normal startup and you'll need to take care of a few specific points. In particular:

    • after-init-hook is run before the init file is loaded.
    • The *scratch* buffer is created before the init file is loaded. You'll have to change its mode explicitly (instead of using initial-major-mode).
    • You'll need to explicitly call package-initialize; it won't be done automatically
  • the path to init.el can be arbitrarily chosen; in particular, the directory in which init.el resides doesn't have to be named .emacs.d. I use this to have for example .emacs.d.23 alongside .emacs.d.24 in order to be able to switch between different versions of emacs (the system I'm using at work is passably outdated, and I can't install emacs 24 on all the machines I use).

  • this workflow doesn't require modifying the environment (and especially the HOME envvar), which can be desirable if you run programs from within emacs, which could be affected by the modified environment).

François Févotte
  • 6,007
  • 1
  • 25
  • 38
  • 1
    This does (in effect) alter the normal execution order, if you are considering the --loaded file to be the init file. For starters, it looks to me as if normal (default) package initialisation won't occur, and after-init-hook will run before the (fake) init file is evaluated. These are things that you can work around, certainly, but be aware that it's not exactly the same thing as Emacs using the specified path as the init file. – phils Dec 10 '14 at 13:31
  • 2
    @phils yes, you're right. This does indeed change the normal execution order, and is not equivalent to using a regular init file. I edited my answer to reflect your point on after-init-hook. But I have to say that although I use this technique all the time, I never encountered any problem with after-init-hook (but I don't use it explicitly, and maybe I'm just lucky that the packages I use don't rely on it). What do you mean by "normal (default) package initialisation won't occur"? – François Févotte Dec 10 '14 at 19:57
  • 1
    I mean that command-line won't call package-initialize in that situation. You would need to call it manually in the fake init file. – phils Dec 10 '14 at 22:07
  • 1
    @phils thanks. I added this to the answer, along with a mention that the initial major mode must also be taken care of specifically. – François Févotte Dec 11 '14 at 07:56
  • This worked perfectly.. Thank you!!! – Stryker Aug 18 '17 at 05:42
43

The basic approach I use for this is to modify $HOME, by running:

env HOME=/path/to/dir emacs

You then use /path/to/dir/.emacs.d

You may wish to also symlink any files or directories of importance in this fake home dir back to the real ones, so that Emacs will see them.

phils
  • 50,977
  • 3
  • 79
  • 122
18

You can symlink ~/.emacs.d, this is what I do

  1. Try to keep my emacs configuration ~/.emacs.d oriented i.e. all emacs related config files should live in that folder

  2. Then I have an ~/.emacs_configs folder where all config folders (basically a folder with a init.el and rest of the configuration) live, so my personal config folder will be ~/emacs_configs/iqbal, a prelude distribution will be in ~/emacs_configs/prelude

  3. Very early in my personal emacs config I set the user-emacs-directory to the full path to my config using the following

    (setq user-emacs-directory (file-truename "~/.emacs.d/"))
    
  4. Then finally I symlink ~/.emacs.d to the configuration I actually want to use, eg. to use my configuration I will do ln -s ~/emacs_configs/iqbal .emacs.d. If you want to tryout some configuration just copy the configuration folder to ~/emacs_configs/whatever_name and change the symlink

The advantage of the 3rd step is that emacs started with my personal configuration can run unaffected even if I change the .emacs.d symlink while emacs it is running.

Another advantage is since the HOME is not changed external programs emacs might need to interact with are unaffected

AdrieanKhisbe
  • 209
  • 1
  • 10
Iqbal Ansari
  • 7,558
  • 1
  • 29
  • 31
  • 1
    Does this mean we can tweak all the separate emacs configs to (setq user-emacs-directory (file-truename "~/.emacs.d/")) so they can all run unaffected simultaneously? – user1011471 Jun 23 '16 at 17:40
  • 1
    In theory yes, but in practice there might be some libraries that hard-code the path to ~/.emacs.d rather than using user-emacs-directory. I have come across atleast one such library but unfortunately cannot remember the name. – Iqbal Ansari Jun 24 '16 at 07:19
12

A configuration that doesn't change HOME or works with symlinks can be found in my answer https://emacs.stackexchange.com/a/20508/934. With this configuration you can change the user-emacs-directory by setting an environment variable:

EMACS_USER_DIRECTORY=~/.emacsenv.d/spacemacs emacs

and this even works with the daemon.

Uwe Koloska
  • 998
  • 9
  • 14
8

In Emacs 29+, set the user-emacs-directory with the flag --init-directory.

Gavin
  • 288
  • 2
  • 8
4

I found this neat solution from EmacsWiki:

emacs -q -l ~/my-init-file.el

(not exactly using a custom directory, but works nicely because you most likely have a single entry file anyway)

phunehehe
  • 157
  • 1
  • 3
4

The patch which allows you to specify .emacs.d location via `EMACS_USER_DIRECTORY' environment variable is available in https://debbugs.gnu.org/cgi/bugreport.cgi?bug=15539 but it's not yet merged.

god
  • 266
  • 1
  • 8
4

Set your var before loading your init file:

emacs -q --eval '(setq alt-conf t)' --load ~/.emacs

Then, in your init-file (in this case ~/.emacs):

(defvar alt-conf nil)

(if alt-conf
    (let ((default-directory "~/src/elisp-test/"))
      (normal-top-level-add-subdirs-to-load-path)
      (various-alt-config-stuff)
      (message "Alternate conf"))
  (message "Regular conf"))
yPhil
  • 973
  • 6
  • 22
  • Very elegant, my favourite system, since everything remains in Emacs' domain, as it should. Thank you. – gsl Oct 17 '17 at 08:26
4

Expanding on the answer from @phils I made this little shell script (called testrun.sh) for testing out my new emacs config. This might make sense to do in other cases as well (for example when testing changes to your init.el that might break emacs).

#!/bin/bash

cd $(dirname "${BASH_SOURCE[0]}") [ -d .testrun ] || mkdir .testrun cd .testrun [ -h .emacs.d ] || ln -s .. .emacs.d

env HOME=pwd emacs

rm .emacs.d cd .. rm -rf .testrun

EDIT(2022-12-09):

  1. I don't know how well the above works, haven't used it for a long time.
  2. In Emacs 29 you can set user-emacs-directory using --init-directory.

From the Changelog:

* Startup Changes in Emacs 29.1

+++ ** Emacs now supports setting 'user-emacs-directory' via '--init-directory'.

Mattias Bengtsson
  • 1,300
  • 1
  • 11
  • 18
2

Here is a little script based on @Phil's answer and comment about changing the HOME environment variable, and then restoring it within Emacs.

#!/bin/bash

# Use it like this:
#   /path/to/this/script  EMACS_USER_DIRECTORY  [OTHER EMACS ARGS]

# You can never be too careful
set -e

# First arg = emacs user directory
#   (get a canonical, absolute path)
EMACS_USER_DIRECTORY=$(readlink -f "$1")
shift
if [ ! -d "${EMACS_USER_DIRECTORY}" ]; then
    echo "Non-existent directory: '${EMACS_USER_DIRECTORY}'"
    exit 1
fi

# Bootstrap directory
BOOTSTRAP=$(mktemp --directory --tmpdir .emacs-bootstrap.XXXXXX)
mkdir "${BOOTSTRAP}/.emacs.d"

# Bootstrap init file
cat >"${BOOTSTRAP}/.emacs.d/init.el" <<EOF
  ;; # Correctly set-up emacs-user-directory
  (setq user-emacs-directory "${EMACS_USER_DIRECTORY}/")
  (setq user-init-file (concat user-emacs-directory "init.el"))

  ;; # Reset the HOME environment variable
  (setenv "HOME" "${HOME}")

  ;; # Load the real init file and clean-up afterwards
  (unwind-protect (load user-init-file)
    (delete-directory "${BOOTSTRAP}" :recursive))
EOF

# Forward remaining arguments to emacs
exec env HOME="${BOOTSTRAP}" emacs "$@"
François Févotte
  • 6,007
  • 1
  • 25
  • 38
1

If the use case is sharing single emacs configuration ".emacs.d" directory across all users of a linux machine then this solution https://emacs.stackexchange.com/a/4258/5488 would work in most cases, but in some cases emacs tries to write temporary files to the user-emacs-directory (such as .ido.last file). In such cases if the shared config directory has write permission to all users then it will work but may not be desired solution as each system user may not want to share the same directory to store temp files. In such case the following solution will be better option.

The common shared config file .emacs.d/init.el should start with

;; should come before calling package-initialize as it will populate
;; everything under common config "~/.emacs.d/elpa"
(setq user-init-file (or load-file-name (buffer-file-name)))
(setq package-user-dir (concat (file-name-directory user-init-file) "elpa"))

(package-initialize)

Make the shared config .emacs.d have read permission to all users(need not have write permissions)

another_user $ emacs -q --load /path/to/shared/config/.emacs.d/init.el

Every user will have his own "~/.emacs.d/" directory but only used to save the temporary files but the packages and other config are loaded from the shared config directory.

Talespin_Kit
  • 445
  • 2
  • 11
1

Chemacs works for me:

https://github.com/plexus/chemacs2

Installation is as simple as:

  1. clone this git repo as your ~/.emacs.d
  2. create a helper file called .emacs-profiles.el
  3. start emacs like this:
<path-to-emacs> --with-profile 25.3

Note if you've hard-coded

"~/.emacs.d/..."

In your init.el universe like I did, you can just replace this with

(concat user-emacs-directory "...") 
0

All above solutions only work partially. Need combine top two solution together, which is:

set emacs user configures so that emacs will overwrite the default home .emacs.d directory

(setq user-init-file (or load-file-name (buffer-file-name)))
(setq user-emacs-directory (file-name-directory user-init-file))

change HOME PATH

alias es='HOME=~/Documents/ emacs -q --load "/Users/zhenlei/Documents/.emacs.d/init.el"'
Muihlinn
  • 2,614
  • 1
  • 15
  • 22
-1

Old post but here is the answer: Most people recommend --init-directory which does not work because you will have conflicts with eln cache.

The solution is to symlink to to ~/.emacs.d or ~/.config/emacs

mv ~/.config/emacs ~/.config/emacs-current
cp -r ~/.config/emacs-current ~/.config/emacs-testing
ln -s ~/.config/emacs-testing ~/.config/emacs

Now you have two different configs up and running. One testing and one working.

If you want to switch configs remove the link. There is also an -f option you can use when using ln.

rm ~/.config/emacs
ln -s ~/.config/emacs-current ~/.config/emacs
ritchie
  • 159
  • 6
  • I just did this to test:
    $ touch emacs-init-dir/init.el
    $ /bin/emacs --init-directory emacs-init-dir/
    $ # Install corfu from Elpa
    $ ls emacs-init-dir/
    auto-save-list  eln-cache  elpa  init.el
    

    So I don't think what you say is correct.

    This comment is totally unreadable because of https://meta.stackexchange.com/questions/143306/newline-in-stackoverflow-comments btw

    – Mattias Bengtsson Jan 28 '24 at 17:48
  • @MattiasBengtsson All you have to do sym link. I updated the answer. – ritchie Mar 17 '24 at 17:35