Understanding the emacsPackage Option in twist.nix Configuration
The emacsPackage option in twist.nix lets you specify which Emacs derivation to use when building your Emacs Lisp package set, enabling version pinning, custom builds, and reproducible environments.
The emacsPackage argument is a core configuration option in the emacs-twist/twist.nix repository. It controls which Emacs binary Twist uses as the foundation for your entire Emacs configuration, affecting how packages are compiled and which built-in libraries are available.
What is the emacsPackage Option?
emacsPackage is a configurable argument defined in pkgs/emacs/default.nix that determines which Emacs derivation Twist should use when building and managing Emacs Lisp packages.
If you omit this option, Twist falls back to the default Emacs package provided by your Nixpkgs set:
emacsPackage ? pkgs.emacs,
(Source: pkgs/emacs/default.nix line 6)
This default ensures that Twist works out-of-the-box with standard Nixpkgs channels, but the option exists precisely so you can override it when you need specific Emacs versions or custom builds.
Why Use the emacsPackage Option?
The emacsPackage option exists to solve three specific problems in Emacs environment management:
Version Control
You can pin a specific Emacs version (e.g., 29.1, master, or a custom snapshot) so that all generated package derivations are built against that exact version. This prevents unexpected breakages when Nixpkgs updates its default Emacs package.
Custom Builds
You may want to use a patched or locally built Emacs (e.g., with different --with-modules flags or native compilation settings). Supplying your own derivation makes Twist honor those customizations throughout the dependency tree.
Testing and CI
The test suites (test/twist.nix, test/flake.nix) explicitly inject different emacsPackage values to verify that Twist works with multiple Emacs builds:
emacsPackage = emacs-ci.packages.${system}.emacs-snapshot;
(Source: test/flake.nix line 61)
How to Configure emacsPackage in twist.nix
Overriding with a Specific Emacs Version (Flake)
When using Twist in a Flake-based configuration, you can override emacsPackage to use a specific Emacs version from an external source like emacs-ci:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
twist = {
url = "github:emacs-twist/twist.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
# Use a specific Emacs snapshot from the emacs-ci overlay
emacs-ci = {
url = "github:nix-community/emacs-ci";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, twist, emacs-ci, ... }:
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
in {
packages.${system}.myEmacsEnv = twist.lib.mkTwist {
pkgs = pkgs;
lockDir = ./twist-lock;
initFiles = [ ./init.el ];
# <<< The important part >>>
emacsPackage = emacs-ci.packages.${system}.emacs-snapshot;
};
};
}
This pattern is used in the official test suite to validate against bleeding-edge Emacs builds.
Using a Custom Emacs Derivation
For locally patched or custom-configured Emacs builds, pass your own derivation:
# custom-emacs.nix
{ pkgs ? import <nixpkgs> { } }:
pkgs.stdenv.mkDerivation {
name = "emacs-custom-29.2";
src = pkgs.fetchFromGitHub {
owner = "emacs-mirror";
repo = "emacs";
rev = "emacs-29.2";
sha256 = "sha256-…";
};
# …standard Emacs build steps, possibly with extra configure flags…
}
# twist.nix (your configuration)
{
pkgs = import <nixpkgs> { };
lockDir = ./twist-lock;
initFiles = [ ./init.el ];
emacsPackage = import ./custom-emacs.nix { inherit pkgs; };
}
Now every package generated by Twist will be compiled against your locally patched Emacs 29.2.
Using the Default Emacs
If you omit the emacsPackage argument entirely, Twist automatically uses the Emacs provided by your Nixpkgs channel:
{
pkgs = import <nixpkgs> { };
lockDir = ./twist-lock;
initFiles = [ ./init.el ];
# No `emacsPackage` → defaults to pkgs.emacs
}
This is the simplest configuration and works well when you don't need specific Emacs versions.
Technical Implementation Details
Inside Twist, the emacsPackage argument is stored as self.emacs and exposed as the top-level attribute emacs of the generated package set:
inherit emacsPackage;
# ...
self.emacs = emacsPackage;
Twist also consults the Emacs version string to determine which built-in packages to include. For example, use-package is only added for Emacs versions older than 29:
extraPackages ?
if builtins.compareVersions emacsPackage.version "29" > 0
then [] else ["use-package"],
(Source: pkgs/emacs/default.nix lines 17-19)
This version-aware behavior ensures that your configuration automatically adapts to the capabilities of your chosen Emacs build.
Summary
emacsPackageis the configuration option that determines which Emacs derivation Twist uses as the foundation for your package set.- Default behavior uses
pkgs.emacsfrom your Nixpkgs channel when no override is specified. - Version pinning allows you to lock your entire configuration to a specific Emacs version (e.g., 29.1, master, or snapshots).
- Custom builds enable you to use patched or locally built Emacs derivations with specific configure flags.
- Internal usage stores the value as
self.emacsand uses it for version-aware package selection (e.g., excludinguse-packageon Emacs 29+).
Frequently Asked Questions
How do I pin a specific Emacs version with emacsPackage?
Pass a specific Emacs derivation to the emacsPackage argument when calling twist.lib.mkTwist. You can source this from nix-community/emacs-ci for snapshots or use pkgs.emacs29 for specific releases. For example: emacsPackage = pkgs.emacs29; or emacsPackage = emacs-ci.packages.${system}.emacs-snapshot;.
What happens if I don't specify emacsPackage?
If you omit the emacsPackage argument, Twist defaults to using pkgs.emacs from your imported Nixpkgs set. This is defined in pkgs/emacs/default.nix with the fallback argument emacsPackage ? pkgs.emacs. Your configuration will use whatever Emacs version is current in your Nixpkgs channel.
Can I use a custom-patched Emacs with twist.nix?
Yes. You can provide any valid Emacs derivation to emacsPackage, including locally patched builds. Create a custom derivation using stdenv.mkDerivation or override the standard Emacs package, then pass it to twist.lib.mkTwist. This ensures all Emacs Lisp packages are compiled against your specific Emacs build flags and patches.
Does emacsPackage affect which packages are available?
Yes, indirectly. Twist uses the Emacs version string to determine which built-in packages to include in the package set. For example, in pkgs/emacs/default.nix, Twist checks if emacsPackage.version is greater than "29" to decide whether to include use-package as an external package (since it's built-in for Emacs 29+).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →