How do you publish an Emacs package with flakes? `outputs.emacsPackages`?

The best way I’ve found so far to publish an emacs package is this:

  outputs = { self }: {
    emacsPackages.default = {
      lib
    , trivialBuild
    , dash # some dependency
    , s # etc
    }: trivialBuild rec {
      ...
      propagatedUserEnvPkgs = [ dash s ];
      buildInputs = propagatedUserEnvPkgs;
      meta.license = lib.licenses.agpl3Only;
    };
  };

example in the wild

Inspired by the wiki: Emacs - NixOS Wiki

But including this in your own Emacs as a consumer of the flake becomes quite hairy, particularly if you don’t want to hard-code those dependencies in the calling form:

((pkgs.lib.callPackageWith (pkgs // epkgs)) org-llm.emacsPackages.default {})

:frowning:

How do other people do it?

I haven’t tried this myself at all, but I probably wouldn’t recommend this. It’s a nonstandard flake output and doesn’t specify architectures.

For this specific case, I suspect providing an overlay that overrides the emacs package set would probably be most ergonomic.

Figuring out what exactly to override in practice is a different question…

2 Likes

I’d be pretty interested to see an example of how to write that overlay because I agree it would be nice, but there are quite a few locations in nixpkgs where you can find the emacs packages set. It’s diretly on nixpkgs, it’s on individual emacs builds, etc. I didn’t try very hard to be fair but there was no “obvious” way to do it.

The lack of architecture is a feature in this case. Emacs code is architecture independent.

I have a feeling the same thing holds for python, haskell, etc? How are those published through flakes?

I looked a little into it, I agree it’s tricky. In theory there should only be a single nixpkgs attribute that needs to be adjusted, though, since all other uses of it should come from callPackage args and therefore be modified by an overlay.

That looks to be true in practice, but the emacs packaging in nixpkgs is very complex. i managed to find a way to apply overrides to the emacs packages set for a specific emacs and have been carrying that in my dotfiles since, but you’d want something a tad more generic: dotfiles/pkgs/applications/emacs/default.nix at 16a823b51613c6d589f5ed94c72b41d558d0f4d2 · TLATER/dotfiles · GitHub

For other languages it’s less complex to my knowledge, since they don’t need to be tied into the ecosystem via a system-wide init file, so you can pretty easily just add a new package normally.

1 Like

On reflection: doesn’t this approach mean that we (people who publish emacs packages as flakes) are all vying for the same namespace? I thought the whole point of flakes was to solve that.

I’m starting to feel like flakes are actually a step back for anything that isn’t a precompiled binary. Is there any official guidance on this issue, do you know?

(PS thanks for taking the time to answer)

The approach outlined by the Automatic package management on the Emacs page of that wiki does mean you get an attribute set (where the attribute names are a namespace), sure.

It’s also possible to build an emacs with an emacs package from a flake without overriding epkgs.

e.g. Here’s a gist with a flake with a very simple elisp package.

e.g. with hello.el:

;;; hello.el --- Description -*- lexical-binding: t; -*-

(defun hello ()
  "Say hello."
  (interactive)
  (message "Hello, world!"))

(provide 'hello)
;;; hello.el ends here

hello.nix:

{
  trivialBuild,
}:
trivialBuild rec {
  pname = "hello";
  version = "v1.0.0";
  src = ./.;
}

a flake.nix providing this hello emacs package:

{
  inputs = {
    flake-utils.url = "github:numtide/flake-utils";
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
  };
  outputs = {
    self,
    flake-utils,
    nixpkgs,
  }:
    flake-utils.lib.eachDefaultSystem
      (system:
        let pkgs = nixpkgs.legacyPackages.${system}; in
        {
          packages.hello =
            pkgs.callPackage ./hello.nix {
              trivialBuild = pkgs.emacs.pkgs.trivialBuild;
            };
        }
      );
}

Using the ‘manual package management’ that NixOS wiki page for Emacs, in some other flake.nix, would be something similar to:

{
  inputs = {
    flake-utils.url = "github:numtide/flake-utils";
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    hello.url = "git+https://gist.github.com/rgoulter/fc8863d085d98276d8d51b1f301e150a";
  };
  outputs = {
    self,
    flake-utils,
    nixpkgs,
    hello,
  }@inputs:
    flake-utils.lib.eachDefaultSystem
      (system:
        let
          pkgs = nixpkgs.legacyPackages.${system};
          hello = inputs.hello.packages.${system}.hello;
        in
        {
          packages.emacs =
            pkgs.emacs.pkgs.emacsWithPackages (epkgs: [
              hello
            ]);
        }
      );
}
1 Like