I'm interested in changing my configuration to use twist.nix instead of nixpkgs for Emacs packages. It took me a while to figure out how to get everything to build so I'd figure I'd just dump out the relevant Nix code in a gist for others to possibly use as a reference. This gist will not be maintained.
Created
November 30, 2025 02:51
-
-
Save adisbladis/63cefc315d6764df657b21e8819efe5a to your computer and use it in GitHub Desktop.
Example building a twist.nix based Emacs closure
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| description = "Using twist.nix to configure emacs"; | |
| inputs = { | |
| nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; | |
| twist.url = "github:emacs-twist/twist.nix"; | |
| # Package registries for Twist | |
| melpa = { | |
| url = "github:melpa/melpa"; | |
| flake = false; | |
| }; | |
| gnu-elpa = { | |
| # Use a GitHub mirror for a higher availability | |
| url = "github:elpa-mirrors/elpa"; | |
| # url = "git+https://git.savannah.gnu.org/git/emacs/elpa.git?ref=main"; | |
| flake = false; | |
| }; | |
| nongnu-elpa = { | |
| # Use a GitHub mirror for a higher availability | |
| url = "github:elpa-mirrors/nongnu"; | |
| # url = "git+https://git.savannah.gnu.org/git/emacs/nongnu.git?ref=main"; | |
| flake = false; | |
| }; | |
| gnu-elpa-archive = { | |
| url = "file+https://elpa.gnu.org/packages/archive-contents"; | |
| flake = false; | |
| }; | |
| nongnu-elpa-archive = { | |
| url = "file+https://elpa.nongnu.org/nongnu/archive-contents"; | |
| flake = false; | |
| }; | |
| }; | |
| outputs = | |
| { | |
| nixpkgs, | |
| twist, | |
| ... | |
| }@inputs: | |
| let | |
| pkgs = nixpkgs.legacyPackages.x86_64-linux; | |
| inherit (nixpkgs) lib; | |
| inherit (pkgs) stdenv cmake libvterm-neovim; | |
| inherit (pkgs) emacs; | |
| registries = [ | |
| { | |
| type = "melpa"; | |
| path = ./recipes; | |
| } | |
| { | |
| type = "melpa"; | |
| path = inputs.melpa.outPath + "/recipes"; | |
| exclude = [ "async" ]; | |
| } | |
| { | |
| type = "elpa"; | |
| path = inputs.gnu-elpa.outPath + "/elpa-packages"; | |
| auto-sync-only = true; | |
| exclude = [ | |
| # Use tarball, as it contains info | |
| "org-transclusion" | |
| "async" | |
| "persist" | |
| ]; | |
| } | |
| { | |
| type = "archive-contents"; | |
| path = inputs.gnu-elpa-archive; | |
| base-url = "https://elpa.gnu.org/packages/"; | |
| } | |
| { | |
| type = "elpa"; | |
| path = inputs.nongnu-elpa.outPath + "/elpa-packages"; | |
| } | |
| { | |
| type = "archive-contents"; | |
| path = inputs.nongnu-elpa-archive; | |
| base-url = "https://elpa.nongnu.org/nongnu/"; | |
| } | |
| ]; | |
| overrides = tfinal: tprev: { | |
| elispPackages = tprev.elispPackages.overrideScope ( | |
| final: prev: { | |
| emacsql-sqlite = prev.emacsql-sqlite.overrideAttrs (old: { | |
| buildInputs = old.buildInputs ++ [ pkgs.sqlite ]; | |
| postBuild = '' | |
| cd sqlite | |
| make | |
| cd .. | |
| ''; | |
| }); | |
| # TODO: Fix build of epdfinfo | |
| pdf-tools = prev.pdf-tools.overrideAttrs (old: { | |
| postInstall = (old.postInstall or "") + '' | |
| cp -a ${pkgs.emacsPackages.pdf-tools}/share/emacs/site-lisp/elpa/pdf-tools-*/epdfinfo $out/share/emacs/site-lisp/epdfinfo | |
| ''; | |
| }); | |
| jinx = prev.jinx.overrideAttrs ( | |
| old: | |
| let | |
| moduleSuffix = stdenv.targetPlatform.extensions.sharedLibrary; | |
| in | |
| { | |
| nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ pkgs.pkg-config ]; | |
| buildInputs = (old.buildInputs or [ ]) ++ [ pkgs.enchant2 ]; | |
| preBuild = '' | |
| NIX_CFLAGS_COMPILE="$($PKG_CONFIG --cflags enchant-2) $NIX_CFLAGS_COMPILE" | |
| $CC -I. -O2 -fPIC -shared -o jinx-mod${moduleSuffix} jinx-mod.c -lenchant-2 | |
| rm *.c *.h | |
| ''; | |
| } | |
| ); | |
| vterm = prev.vterm.overrideAttrs (old: { | |
| nativeBuildInputs = [ | |
| cmake | |
| stdenv.cc | |
| ]; | |
| buildInputs = old.buildInputs ++ [ libvterm-neovim ]; | |
| cmakeFlags = [ "-DEMACS_SOURCE=${emacs.src}" ]; | |
| preBuild = '' | |
| mkdir -p build | |
| cd build | |
| cmake .. | |
| make | |
| install -m444 -t . ../*.so | |
| install -m600 -t . ../*.el | |
| cp -r -t . ../etc | |
| rm -rf {CMake*,build,*.c,*.h,Makefile,*.cmake} | |
| ''; | |
| }); | |
| } | |
| ); | |
| }; | |
| env' = twist.lib.makeEnv { | |
| inherit pkgs registries; | |
| emacsPackage = emacs; | |
| lockDir = ./lock; | |
| initFiles = [ | |
| ./emacs.el | |
| ./exwm.el | |
| ]; | |
| inputOverrides = { | |
| helm = _: prev: { | |
| packageRequires = prev.packageRequires // { | |
| async = "0"; | |
| }; | |
| files = builtins.removeAttrs prev.files [ | |
| "helm-packages.el" | |
| ]; | |
| }; | |
| voicemacs = _: prev: { | |
| packageRequires = { | |
| avy = "0"; | |
| bind-key = "0"; | |
| cl-lib = "0"; | |
| company = "0"; | |
| company-quickhelp = "0"; | |
| default-text-scale = "0"; | |
| f = "0"; | |
| helm = "0"; | |
| json-rpc-server = "0"; | |
| nav-flash = "0"; | |
| porthole = "0"; | |
| yasnippet = "0"; | |
| el-patch = "0"; | |
| } // prev.packageRequires; | |
| }; | |
| }; | |
| initialLibraries = [ | |
| "cl-lib" | |
| "jsonrpc" | |
| "let-alist" | |
| "map" | |
| "org" | |
| "seq" | |
| ]; | |
| extraSiteStartElisp = '' | |
| (add-to-list 'treesit-extra-load-path "${ | |
| pkgs.emacs.pkgs.treesit-grammars.with-grammars ( | |
| _: | |
| pkgs.tree-sitter.allGrammars | |
| ) | |
| }/lib/") | |
| ''; | |
| initParser = twist.lib.parseUsePackages { inherit lib; } { alwaysEnsure = true; }; | |
| }; | |
| env = env'.overrideScope overrides; | |
| in | |
| { | |
| packages.x86_64-linux.emacs = env; | |
| }; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (voicemacs :fetcher github :repo "jcaw/voicemacs") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment