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 reproducible 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.3.0";
src = fetchurl {
url = "https://raw.githubusercontent.com/jeffkreeftmeijer/ox-html-markdown-style-footnotes.el/8351691b28f79fc40b90eea238c1f1ec5b0ff457/ox-html-markdown-style-footnotes.el";
sha256 = "sha256-JY0xIIJa2sPfCVXpwCdoUAM2UFk+Iih7M6E8q4GwZPA=";
};
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 fetchFromGitHub in this specific scenario.3
However, with this package being contained in a single file, the simplest option will suffice.
Also, an advantage of using fetchurl is that it can fetch from anywhere.4
Finally, update the flake to include the newly-added derivation5:
{
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 searchcommand: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
sha256value, 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=↩︎The
fetchFromGitHubhelper is a convenience function that removes some of the boilerplate fromfetchurlwhen fetching from GitHub.↩︎{ lib, fetchFromGitHub, trivialBuild }: trivialBuild { pname = "ox-html-markdown-style-footnotes"; version = "0.2.0"; src = fetchFromGitHub { owner = "jeffkreeftmeijer"; repo = "ox-html-markdown-style-footnotes.el"; rev = "8351691b28f79fc40b90eea238c1f1ec5b0ff457"; sha256 = "sha256-JY0xIIJa2sPfCVXpwCdoUAM2UFk+Iih7M6E8q4GwZPA="; }; 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; }; }For example, nixpkgs uses the
elpaBuildhelper to include Emacs packages into the registry. To get a different version of a package from GNU Elpa, combine it withfetchurl:↩︎((emacsPackagesFor emacs-nox).emacsWithPackages (epkgs: [ (epkgs.elpaBuild { pname = "ef-themes"; ename = "ef-themes"; version = "1.11.0"; src = fetchurl { url = "https://elpa.gnu.org/packages/ef-themes-1.11.0.tar"; sha256 = "086d2fmgzgnjil97zjn2i0ii81dq9l8rr859j0kyaxiy83r27rqb"; }; }) ]))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.3.0"; src = fetchurl { url = "https://raw.githubusercontent.com/jeffkreeftmeijer/ox-html-markdown-style-footnotes.el/8351691b28f79fc40b90eea238c1f1ec5b0ff457/ox-html-markdown-style-footnotes.el"; sha256 = "sha256-JY0xIIJa2sPfCVXpwCdoUAM2UFk+Iih7M6E8q4GwZPA="; }; 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; }; }) ])) ]; }; }); }