All of the messiness with configuring email with Emacs reminded me that there might be a better way using Nix. Let's give it a try with some other packages.

Follow the quick install instructions to get a nix command.

Home Manager "provides a basic system for managing a user environment using the Nix package manager." I'm not sure what exactly that means over using just nix-shell, but it seems to be related to managing configuration dotfiles for the software installed using home-manager. The Home Manager manual say you can install with:

nix-shell '<home-manager>' -A install

There's also a note that we're supposed to source the

$HOME/.nix-profile/etc/profile.d/hm-session-vars.sh

file in my shell configuration, but that this only supports Bash or ZSH, not fish.

Fishing digression

Luckily, we can use the foreign-env plugin in fish (installed using Oh My Fish), which provides an fenv function for sourcing variables using bash syntax. Now we can add a ~/.config/fish/nix.fish:

if test -e '/home/shaun/.nix-profile/etc/profile.d/nix.sh'
    fenv source '/home/shaun/.nix-profile/etc/profile.d/nix.sh'
end

if test -e '/home/shaun/.nix-profile/etc/profile.d/hm-session-vars.sh'
    fenv source '/home/shaun/.nix-profile/etc/profile.d/hm-session-vars.sh'
end

and call this from ~/.config/fish/config.fish

. ~/.config/fish/nix.fish

and after restarting the fish session, the variables are set:

❯ export |grep HM_SESS
__HM_SESS_VARS_SOURCED 1

Great. (There's also a ~/.nix-profile/etc/profile.d/nix-daemon.sh file too. Do I need that? Not sourcing it hasn't seemed to cause an issue yet.)

Configuring packages with home-manager

Installing home-manager created a ~/.config/nixpkgs/home.nix file.

{ config, pkgs, ... }:

{
  # Let Home Manager install and manage itself.
  programs.home-manager.enable = true;

  # Home Manager needs a bit of information about you and the
  # paths it should manage.
  home.username = "shaun";
  home.homeDirectory = "/home/shaun";

  # This value determines the Home Manager release that your
  # configuration is compatible with. This helps avoid breakage
  # when a new Home Manager release introduces backwards
  # incompatible changes.
  #
  # You can update Home Manager without changing this value. See
  # the Home Manager release notes for a list of state version
  # changes in each release.
  home.stateVersion = "21.05";
}

Let's start with something (hopefully) simple and install Emacs, since I had been building from source to get Emacs 27. Is Emacs 27 available?

❯ nix search emacs27
 * nixpkgs.emacs27 (emacs-27.1)
  The extensible, customizable GNU text editor

 * nixpkgs.emacs27-nox (emacs-nox-27.1)
  The extensible, customizable GNU text editor

Ok, so let's try to install it by adding the appropriate incantations to ~/.config/nixpkgs/home.nix. These "options" are documented at https://rycee.gitlab.io/home-manager/options.html. I don't know why most other tutorials don't link to this, and I only found this in a Reddit thread. The Home Manager documentation links to it in the Table of Contents, but I skipped right over it the first time.

Some of the tutorials list packages in a home.packages expression, which defines " The set of packages to appear in the user environment."

home.packages = with pkgs; [
  emacs27
];

This sounds reasonable. On the other hand, there's also a programs.emacs group, so I could do:

programs.emacs = {
  enable = true;
};

I tried both. That caused a conflict:

❯ home-manager switch
these derivations will be built:
  /nix/store/1q13zc9ybdbvm918viav0v1jrlynk8q2-home-manager-path.drv
  /nix/store/pfcddd9ghlj7ixmcvbpkqcxysq9nl9pk-activation-script.drv
  /nix/store/cc09h27dkycp1jy8sprn1ng1s8y4vmx7-home-manager-generation.drv
building '/nix/store/1q13zc9ybdbvm918viav0v1jrlynk8q2-home-manager-path.drv'...
collision between `/nix/store/m1mgx2y1drqmx90m2x6aql00biigzi89-emacs-27.1/bin/ctags' and `/nix/store/26zz4k67p8rpiqsgg82dvssky3k0d49r-emacs-with-packages-27.1/bin/ctags'
builder for '/nix/store/1q13zc9ybdbvm918viav0v1jrlynk8q2-home-manager-path.drv' failed with exit code 25
cannot build derivation '/nix/store/cc09h27dkycp1jy8sprn1ng1s8y4vmx7-home-manager-generation.drv': 1 dependencies couldn't be built
error: build of '/nix/store/cc09h27dkycp1jy8sprn1ng1s8y4vmx7-home-manager-generation.drv' failed

For more details:

❯ home-manager switch
these derivations will be built:
  /nix/store/1q13zc9ybdbvm918viav0v1jrlynk8q2-home-manager-path.drv
  /nix/store/pfcddd9ghlj7ixmcvbpkqcxysq9nl9pk-activation-script.drv
  /nix/store/cc09h27dkycp1jy8sprn1ng1s8y4vmx7-home-manager-generation.drv
building '/nix/store/1q13zc9ybdbvm918viav0v1jrlynk8q2-home-manager-path.drv'...
collision between `/nix/store/m1mgx2y1drqmx90m2x6aql00biigzi89-emacs-27.1/bin/ctags' and `/nix/store/26zz4k67p8rpiqsgg82dvssky3k0d49r-emacs-with-packages-27.1/bin/ctags'
builder for '/nix/store/1q13zc9ybdbvm918viav0v1jrlynk8q2-home-manager-path.drv' failed with exit code 25
cannot build derivation '/nix/store/cc09h27dkycp1jy8sprn1ng1s8y4vmx7-home-manager-generation.drv': 1 dependencies couldn't be built
error: build of '/nix/store/cc09h27dkycp1jy8sprn1ng1s8y4vmx7-home-manager-generation.drv' failed

Ok, so some clash between emacs-27.1 and emacs-with-packages-27.1. Let's get rid of the entry from home.packages:

❯ home-manager switch
/nix/store/lidm821d00nn86im15gic2gjdh984i3w-home-manager-generation
Starting home manager activation
Activating checkFilesChanged
Activating checkLinkTargets
Activating writeBoundary
Activating installPackages
replacing old 'home-manager-path'
installing 'home-manager-path'
Activating linkGeneration
Cleaning up orphan links from /home/shaun
No change so reusing latest profile generation 9
Creating home file links in /home/shaun
Activating onFilesChange
Activating reloadSystemd

Looks promising. Let's try it:

❯ which emacs
/home/shaun/.nix-profile/bin/emacs

Running emacs at the prompt starts GUI Emacs, as desired. Wonderful.

Unfortunately, this new Emacs installation doesn't appear when I search for "emacs" using the Gnome Activities overlay by pressing Super.

Digging around led me to some instructions on using Emacs as Default Editor, which indicated that I need an ~/.local/share/applications/emacs.desktop file. Adapting the example from that page (which was configured for EmacsClient):

[Desktop Entry]
Name=Emacs
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=emacs
Icon=emacs
Type=Application
Terminal=false
Categories=Development;TextEditor;
StartupWMClass=Emacs

And now I can launch emacs from the Activity overview, complete with an Emacs icon. It would have been nice if there was a home-manager option that would create this file, but I couldn't find one. So let's just cram it into a generic config file option: home.file:

  home.file.emacsDesktop = {
    target = "./.local/share/applications/emacs.desktop";
    text = "[Desktop Entry]
Name=Emacs
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=emacs
Icon=emacs
Type=Application
Terminal=false
Categories=Development;TextEditor;
StartupWMClass=Emacs";
  };

And it still works! This configuration process wasn't so bad. Next up, mail configuration using home-manager.

Edit

It turns out home-manager did create an emacs.desktop file, which is in ~/.nix-profile/share/applications. The problem is that my desktop environment wasn't looking for these XDG files there. I tried adding ~/.nix-profile/share/applications to XDG_DATA_DIRS using home.sessionVariables, but according to this Github thread, that's too late in the process. On the other hand, this post on the thread seemed to do what I wanted. so now home.nix includes these lines (xdg.mime.enable is true by default so unnecessary to add):

xdg.enable = true;
targets.genericLinux.enable = true;

And then I edited .profile to include:

if [ -e /home/shaun/.nix-profile/share ] ; then
    XDG_DATA_DIRS="/home/shaun/.nix-profile/share:$XDG_DATA_DIRS"
fi

Log out, log in, and it works perfectly. So now I can delete the expression that manually generates the emacs.desktop file.