This repository contains a Nix derivation of Emacs. It does not depend on NixOS (so can be used with the Nix package manager) and is platform independent.
The code contained in this repository is licensed under GNU General Public License.
Its goal is a reproducible build of an emacs
binary that
includes all of its Elisp packages. A standard Emacs setup (i.e. without Nix) would
download extra Elisp packages from Melpa on startup.
For example, consider the following statement in an init.el
:
(use-package magit)
This would download the .el
files of the magit
package into the
~~/.emacs.d/melpa~ directory. These would be obtained from the
HEAD
of the magit
git repository.
In contrast, this project downloads magit
when the emacs
binary
is built. Nothing is downloaded when emacs
is run.
In addition, the version of magit
is not the HEAD
, but a fixed
commit hash.
This Emacs Nix setup is a little unconventional. Before going into details, let’s first explore the standard setup documented in the NixOS manual.
This shows how to write a nix derivation for emacs that includes
packages from melpa
. Note that there is no elisp code ⸺ each
package is itself a nix derivation containing .el
files.
While this approach is a working solution, it isn’t as easy to use
and configure as use-package
. Instead, this repository uses the
Emacs overlay to get the best of both worlds by writing
use-package
expressions in .el
files and parsing these into nix
derivations.
The expression (use-package magit)
in the
melpa-mirror-packages.el
file is parsed into a magit
nix
derivation. This outputs .el
files that are ultimately placed in
the site-lisp
directory.
Extra .el
files (ones that aren’t packaged in MELPA) are obtained
from their git repositories by using the build-emacs-package
function. These are byte compiled at build time to produce faster
.elc
files.
Run the following command on every time this code is modified.
nix-env -i -f default.nix
M-x nix-repl
Import the expression (function) from defalt.nix
.
func = import ./default.nix
Call the function.
result = func {}
This produces a lazy set.
Build the derivation to evaluate the set.
:b result
This should produce an output
this derivation produced the following outputs: out -> /nix/store/l20amhsjrhni6kaqi8w515nv7x2xcryc-emacs-site-lisp
The structure of which is:
> tree -L 4 /nix/store/ghqamfzd5s95k1fm4ynjwhpnr70m1wbm-emacs-site-lisp/ /nix/store/ghqamfzd5s95k1fm4ynjwhpnr70m1wbm-emacs-site-lisp/ ├── bin │ ├── ctags -> /nix/store/r7wlhbhqpk1ixbnddnj2xwhmiy10rl7i-emacs-with-packages-27.2/bin/ctags │ ├── ebrowse -> /nix/store/r7wlhbhqpk1ixbnddnj2xwhmiy10rl7i-emacs-with-packages-27.2/bin/ebrowse │ ├── emacs -> /nix/store/r7wlhbhqpk1ixbnddnj2xwhmiy10rl7i-emacs-with-packages-27.2/bin/emacs │ ├── emacs-27.2 -> /nix/store/r7wlhbhqpk1ixbnddnj2xwhmiy10rl7i-emacs-with-packages-27.2/bin/emacs-27.2 │ ├── emacsclient -> /nix/store/r7wlhbhqpk1ixbnddnj2xwhmiy10rl7i-emacs-with-packages-27.2/bin/emacsclient │ └── etags -> /nix/store/r7wlhbhqpk1ixbnddnj2xwhmiy10rl7i-emacs-with-packages-27.2/bin/etags └── share ├── applications -> /nix/store/zvpy3f4bhav21c9r7nf1ifp38j3i2j31-emacs-27.2/share/applications ├── emacs │ └── site-lisp │ ├── browse-repositories │ ├── cartography.el -> /nix/store/732l3jvnfimkg4cy6ak288g8xwkpbnys-emacs-site/share/emacs/site-lisp/cartography.el │ ├── coe │ ├── dashboard.el -> /nix/store/732l3jvnfimkg4cy6ak288g8xwkpbnys-emacs-site/share/emacs/site-lisp/dashboard.el │ ├── fandango │ ├── light.el -> /nix/store/732l3jvnfimkg4cy6ak288g8xwkpbnys-emacs-site/share/emacs/site-lisp/light.el │ ├── melpa-mirror-packages.el -> /nix/store/732l3jvnfimkg4cy6ak288g8xwkpbnys-emacs-site/share/emacs/site-lisp/melpa-mirror-packages.el │ ├── melpa-mirror-packages.elc -> /nix/store/732l3jvnfimkg4cy6ak288g8xwkpbnys-emacs-site/share/emacs/site-lisp/melpa-mirror-packages.elc │ ├── mill.el -> /nix/store/732l3jvnfimkg4cy6ak288g8xwkpbnys-emacs-site/share/emacs/site-lisp/mill.el │ ├── pollen-mode │ ├── project+.el -> /nix/store/732l3jvnfimkg4cy6ak288g8xwkpbnys-emacs-site/share/emacs/site-lisp/project+.el │ ├── project+.elc -> /nix/store/732l3jvnfimkg4cy6ak288g8xwkpbnys-emacs-site/share/emacs/site-lisp/project+.elc │ ├── site-packages.el -> /nix/store/732l3jvnfimkg4cy6ak288g8xwkpbnys-emacs-site/share/emacs/site-lisp/site-packages.el │ └── subed ├── icons -> /nix/store/zvpy3f4bhav21c9r7nf1ifp38j3i2j31-emacs-27.2/share/icons ├── info -> /nix/store/zvpy3f4bhav21c9r7nf1ifp38j3i2j31-emacs-27.2/share/info └── man -> /nix/store/zvpy3f4bhav21c9r7nf1ifp38j3i2j31-emacs-27.2/share/man
The site-init.el
and site-packages.el
are bootstrap mechanisms because the emacs
created by nix looks for anything under share/site/elisp
in NIX_PROFILES
. We
make use of that fact to have a minimal init.el
.
Add the following to the init.el
:
;; -*- lexical-binding: t; -*-
(setq gc-cons-threshold (* 50 1000 1000))
(require 'melpa-mirror-packages)
(require 'site-packages)
(evil-mode)
(setq gc-cons-threshold (* 2 1000 1000))
The Emacs in this repository is tied to a given commit hash of the emacs overlay. This commit is a reference to a point in time for all the repositories in MELPA.
To update Emacs, modify this commit hash and rebuild.
overlays = [
(import (builtins.fetchGit {
url = "https://github.com/nix-community/emacs-overlay.git";
rev = "e3c046d45ce2d9aa07fd54a330f42edc352e12d5";
ref = "master";
}))
];
This assumes the font configuration
fonts = with pkgs; [ source-code-pro source-sans-pro source-serif-pro ibm-plex ];
Fonts cause problems because they need to be installed under specific directories
if we are not using NixOS. Font config looks under $HOME/.local/share/fonts
for
any fonts we might want.
This is slightly unfortunate, but we can still bundle the fonts that we want available using Nix by creating symlinks between the nix store font and the font that we care about.
See imperative installation of user fonts, NixOs Wiki.
For example, to install FiraCode
:
# Download the font
font=$(nix-build --no-out-link '<nixpkgs>' -A fira-code)/share/fonts/truetype/*
# Copy and pase the directory
mkdir ~/.local/share/fonts
cp $font ~/.local/share/fonts
fc-cache
# Verify that the font has been installed
fc-list -v | grep -i fira