Add packages to Emacs PATH?

I’m trying to override Emacs to include additional packages into its PATH (for example pandoc or graphviz) in order have them available for Org-mode but to not pollute my system environment with them. What I tried is (adding makeWrapper to nativeBuildInputs is not needed because it is already in the derivation included in Nixpkgs):

  myEmacsPackage = pkgs.emacsUnstable.overrideAttrs (finalAttrs: prevAttrs: {
    postInstall = ''
      wrapProgram $out/bin/emacs --prefix PATH : "${
        lib.makeBinPath [ pkgs.pandoc ]
      }"
    '';
  });

This fails with the strange error message from (bash):

patching script interpreter paths in /nix/store/0kpx40di7gh4p3hxwkia90b6l5sr0csr-emacs-unstable-28.2
/nix/store/0kpx40di7gh4p3hxwkia90b6l5sr0csr-emacs-unstable-28.2/libexec/emacs/28.2/x86_64-pc-linux-gnu/rcs2log: interpreter directive changed from "#! /bin/sh" to "/nix/store/pj1hnyxhcsw1krmhnbb9rjvqssbzliw8-bash-5.2-p15/bin/sh"
strip is /nix/store/iiq295j1z4q1sxmdbrl2j8ma3l5ns4wr-gcc-wrapper-11.3.0/bin/strip
stripping (with command strip and flags -S) in  /nix/store/0kpx40di7gh4p3hxwkia90b6l5sr0csr-emacs-unstable-28.2/lib /nix/store/0kpx40di7gh4p3hxwkia90b6l5sr0csr-emacs-unstable-28.2/libexec /nix/store/0kpx40di7gh4p3hxwkia90b6l5sr0csr-emacs-unstable-28.2/bin
patchelf: not an ELF executable
/nix/store/chilfhdcsnmwjl7igrw26j1lrc0zar35-stdenv-linux/setup: line 129: pop_var_context: head of shell_variables not a function context

I’m not sure if that matters (I don’t think so), but I’m using the emacs-overlay.

The derivation in Nixpkgs of Emacs already contains a postInstall phase. Does the addition of my postInstall overwrite the existing one in the Emacs derivation?

What is proper way to achieve my goal of adding additional packages to Emacs PATH?

I already thought about using symlinkJoin but I’m not sure what would be the most idiomatic way to do it.

Thanks

Yes.

Probably what you’re doing, but you’ll need to access the previous postInstall from prevAttrs, and then add on to it.

1 Like

What is proper way to achieve my goal of adding additional packages to Emacs PATH?

  1. Use a wrapper script. You don’t want to have to rebuild emacs just because you decide you need to add another application. You can even generate a .desktop file so that your application launcher runs your script. BONUS: if you name you wrapper script em you save 60% every time you launch emacs!
  2. In the wrapper script, don’t just throw things in the PATH - instead, use symlinkJoin to generate a new environment with all your binaries and reference that instead.
5 Likes

Ok, that makes sense. For learning purposes I will try to access the previous postInstall.

Ok, this is what I was thinking already about because I tried the several times and each one took over 10 minutes. I already have a script called em (in particular it is a shell function) which is either using vterm_cmd or emacsclient :slight_smile: I think I will create script derivation in combination with symlinkJoin.

Update 2023-01-29 (later): This is what I came up with:

{ pkgs, lib, ... }:

let
  emacsBinary = pkgs.emacsUnstable.override {
    # Override options.
    withToolkitScrollBars = false;
    withAthena = true;
  };

  emacsPkgs = pkgs.dontRecurseIntoAttrs (pkgs.emacsPackagesFor emacsBinary);

  unwrappedEmacs = (pkgs.emacsPackagesFor emacsBinary).emacsWithPackages (epkgs: [
    # Emacs packages here.
  ]);

  wrappedEmacs = let binPkgs = with pkgs; [ pandoc graphviz nixfmt ];
  in pkgs.symlinkJoin {
    name = "emacs-wrapped";
    paths = [ unwrappedEmacs ];
    nativeBuildInputs = [ pkgs.makeWrapper ];
    postBuild = ''
      rm $out/bin/emacs-${emacsBinary.version};
      wrapProgram $out/bin/emacs --suffix PATH : "${lib.makeBinPath (binPkgs)}"
    '';
  };
in { inherit unwrappedEmacs wrappedEmacs; }

I put this into a file emacs.nix, consume it via callPackage in my configuration and use wrappedEmacs as a systemd service. It works nicely so far.