Aside from managing an Emacs installation, Nix can also bundle Emacs packages.
For example, to produce a development shell that has Emacs available with Evil mode installed, use emacsWithPackages
.
In this example flake, the build inputs include a version of Emacs based on emacs-nox
with the Evil package added:
{ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; flake-utils.url = "github:numtide/flake-utils"; }; outputs = { nixpkgs, flake-utils, ... } : flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { inherit system; }; in { devShells.default = pkgs.mkShell { buildInputs = with pkgs; [ ((emacsPackagesFor emacs-nox).emacsWithPackages(epkgs: [ epkgs.evil ])) ]; }; }); }
With the flake in place, a development shell is started by running nix develop
, which opens a shell with Emacs installed and available.
To start Emacs directly, without having to start a sub shell first, use the --command
flag:
nix develop --command emacs
Within this version of Emacs, the packages defined in the flake’s build inputs are available. This allows for reproducable setups of local development environments, or when using Emacs for batch processing, for example.
Nixpkgs automatically creates derivations based on the recipes available in Elpa, Elpa-devel, NonGNU Elpa and Melpa, which means most packages are available in Nix’s emacsPackage set1.
An Emacs package derivation
Emacs packages can be used even if they’re not available through Nixpkgs by writing a derivation.
For example, the ox-html-markdown-style-footnotes package is not available through any package manager, but it is useful for generating HTML from Org documents through batch processing. To bundle it with Emacs, write a derivation:
{ lib, fetchurl, trivialBuild }: trivialBuild { pname = "ox-html-markdown-style-footnotes"; version = "0.2.0"; src = fetchurl { url = "https://raw.githubusercontent.com/jeffkreeftmeijer/ox-html-markdown-style-footnotes.el/0.2.0/ox-html-markdown-style-footnotes.el"; sha256 = "sha256-S+lzFpGY44OgXAeM9Qzdhvceh8DvvOFiw5tgXoXDrsQ="; }; meta = with lib; { description = "Markdown-style footnotes for ox-html.el"; homepage = "https://jeffkreeftmeijer.com/ox-html-markdown-style-footnotes/"; license = licenses.gpl3; platforms = platforms.all; }; }
The derivation only sets some general information about the package, like the name, file URL and fingerprint2.
The trivialBuild
function takes care of the rest.
This example uses the fetchurl fetcher, which could be swapped out for a more sophisticated option like fetchgit, fetchFromGitea or fetchFromGithub. However, with this package being contained in a single file, the simplest option will suffice.
Finally, update the flake to include the newly-added derivation3:
{ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; flake-utils.url = "github:numtide/flake-utils"; }; outputs = { nixpkgs, flake-utils, ... } : flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { inherit system; }; in { devShells.default = pkgs.mkShell { buildInputs = with pkgs; [ ((emacsPackagesFor emacs-nox).emacsWithPackages(epkgs: [ epkgs.evil (epkgs.callPackage ./ox-html-markdown-style-footnotes.nix {}) ])) ]; }; }); }
To determine if an Emacs package is available through Nixpkgs, use search.nixos.org, or the
nix search
command:nix search nixpkgs emacsPackages.evil
* legacyPackages.aarch64-darwin.emacsPackages.evil (20231106.1213) * legacyPackages.aarch64-darwin.emacsPackages.evil-anzu (20220911.1939) * legacyPackages.aarch64-darwin.emacsPackages.evil-args (20220125.1626) * legacyPackages.aarch64-darwin.emacsPackages.evil-avy (20150908.748) ...
↩︎To get the
sha256
value, simply omit it when running for the first time and take the correct value from the output:warning: found empty hash, assuming 'sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' error: hash mismatch in fixed-output derivation '/nix/store/144z7a0c6jckqrkrmdnaqdfwi37vqs8m-ox-html-markdown-style-footnotes.el.drv': specified: sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= got: sha256-S+lzFpGY44OgXAeM9Qzdhvceh8DvvOFiw5tgXoXDrsQ=
↩︎In a pinch, the derivation can also be placed directly in the flake, as it’s just a function:
{ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; flake-utils.url = "github:numtide/flake-utils"; }; outputs = { nixpkgs, flake-utils, ... } : flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { inherit system; }; in { devShells.default = pkgs.mkShell { buildInputs = with pkgs; [ ((emacsPackagesFor emacs-nox).emacsWithPackages(epkgs: [ epkgs.evil (epkgs.trivialBuild { pname = "ox-html-markdown-style-footnotes"; version = "0.2.0"; src = fetchurl { url = "https://raw.githubusercontent.com/jeffkreeftmeijer/ox-html-markdown-style-footnotes.el/0.2.0/ox-html-markdown-style-footnotes.el"; sha256 = "sha256-S+lzFpGY44OgXAeM9Qzdhvceh8DvvOFiw5tgXoXDrsQ="; }; meta = with lib; { description = "Markdown-style footnotes for ox-html.el"; homepage = "https://jeffkreeftmeijer.com/ox-html-markdown-style-footnotes/"; license = licenses.gpl3; platforms = platforms.all; }; }) ])) ]; }; }); }