Proposal: Make it easier to define manpages/shell completions

Every package I’ve looked at that installs manpages or shell completions has to do so with a postInstall hook that either uses install or mkdir+cp to copy the files into special locations. This can be rather annoying to deal with, and I always have to look at an existing package for reference as to the paths to use (at least for completions; manpage location is a little more well-known).

To that end, I’d like to propose an addition to mkDerivation (or maybe to the default builder) that lets you specify the source locations for manpages and shell completions and it will copy them into the appropriate directories for you. This could look something like

manpages = [ "docs/foobar.1" ];
shellCompletions = {
    bash = [ "share/completions/foobar.bash" ];
    fish = [ "share/completions/foobar.fish" ];
};

If you specify a path instead of a string then that would act like any other path and be relative to the derivation, in case a derivation wants to bundle the manpage or completions directly.

Open questions:

Should this specifically be a feature of mkDerivation and be used to synthesize a default postInstall, such that a manually-specified postInstall would disable this behavior? That would be a simple way to implement it, but likely confusing.

Should this instead be a feature of the builder and run as part of installPhase? That makes the most sense to me but if a derivation overrides or disables installPhase then it wouldn’t run. It also means any package that provides a custom builder would lose this functionality.

I suppose we could also have it be part of mkDerivation and be prepended to any specified postInstall.

Or is there an alternative way to do this that would be better?

3 Likes

Shouldn’t a well configured make install take care of this?

But if it doesn’t, you could try to write a setup hook for it.

A well-configured make install won’t take care of it if the project is not using make. For example, I maintain a few packages for tools written in Rust which include manpages and/or shell completions but there’s no Makefile involved.

That said, a setup hook sounds like it has potential. These don’t seem to be documented very well, but it sounds like I can use a setup hook to append a function to an array variable postInstallHooks and then that function will get invoked at post-install time. If so, that would work, though it requires any derivation. Still, it’s worth installing. I assume that when the setup hook is run, the active environment will include the attributes set on the derivation, though I’m also not sure offhand how attrsets are exposed to the builder environment.

One thing I would like to have too is autocompletion even within a nix-shell -A <PKG> .
I can imagine loading the completion from the build of PKG, but being able to load the completion from the source could be an alternative (each with its own pros and cons).

I just looked into the feasibility of defining this as a setup hook and unfortunately doing so means I can’t define shell completions as

shellCompletions = {
    bash = [ "share/completions/foobar.bash" ];
    fish = [ "share/completions/foobar.fish" ];
};

as sets cannot be coerced to strings when exposing the derivation attributes to the environment. Maybe I could hack something together by defining a custom function so you’d say

shellCompletions = installShellFiles.completions {
    bash = [ "share/completions/foobar.bash" ];
    fish = [ "share/completions/foobar.fish" ];
};

and have that be a function that synthesizes some parseable format (maybe JSON, though that’s not really parseable from bash), but this seems rather awkward.

Alternatively it could be something like

bashCompletions = [ "share/completions/foobar.bash" ];
fishCompletions = [ "share/completions/foobar.fish" ];

but this just seems less than ideal to me.


Edit: I suppose I could shrink the custom function version to

shellCompletions = installShellFiles {
    bash = [ "share/completions/foobar.bash" ];
    fish = [ "share/completions/foobar.fish" ];
};

by making the installShellFiles derivation also a functor. Slightly less awkward, though I don’t know if there’s any issues with having a derivation that’s also a functor. And of course no matter the solution you also have to write nativeBuildInputs = [ installShellFiles ]; to even get the setup hook . Not the end of the world, just annoying.

I went ahead and implemented this (using the shellCompletions = installShellFiles { … } approach). It doesn’t do anything special with nix-shell but it works for building packages in my testing.

https://github.com/NixOS/nixpkgs/pull/65211

1 Like

or you could put the information into meta / passthru which can contain arbitrary types (Though none of it fits). It could also be stripped down or converted into a string/bash array in mkDerivation.

If I put it in the meta or passthru the shell hook can’t see it.

My original idea was to put this functionality in mkDerivation itself but @timokau suggested it should be a setup hook.

A well-configured make install won’t take care of it if the project
is not using make. For example, I maintain a few packages for tools
written in Rust which include manpages and/or shell completions but
there’s no Makefile involved.

Does (for example) Cargo not have a convention for installing manpages?
If not, that strikes me as quite the omission…

Cargo is not a package management system. The only thing it installs is binaries, and even there you have to pass a --force flag if you want to overwrite any existing binaries (which is to say, it doesn’t natively handle updates).

There’s been discussions about extending this to installing manpages, but it hasn’t gotten enough support.