Difference between nix-shell and "nix run"?

#1

I’m trying to define common Nix shells as overlays, but somehow nix run refuses to use them.

For example:

self: super:

{
  go-dev-shell = mkShell {
    buildInputs = with self; [
      delve
      go
      gotools
    ];

    shellHook = ''
      export GO111MODULE=on
      export GOPATH=$HOME/.go
    '';
  };
}

nix-shell -A go-dev-shell '<nixpkgs>' works fine, but nix run -f '<nixpkgs>' go-dev-shell always complains about This derivation is not meant to be built, aborting.

From what I’ve learned, mkShell is a simple wrapper around stdenv.mkDerivation that disables all build phases (see pkgs/build-support/mkshell/default.nix) - there shouldn’t be any special handling for mkShell in nix-shell, right?

Therefore I cannot understand what is the difference between nix-shell and nix run :frowning:.

1 Like
#2

nix-shell gives you an environment where the dependencies of the supplied expression are available, not the package itself.
That’s why derivations created with mkShell cannot be built.

nix-shell has the -p flag that behaves as you expected (i.e. provides a shell with the given package). e.g.:

nix-shell -p gimp --run gimp
#3

Yeah. I understand that mkShell derivation cannot be built. Problem is: how does nix-shell treat it differently while nix run doesn’t?

Or: I want to replace nix-shell with nix run. Is that possible?

#4

nix-shell -A go-dev-shell '<nixpkgs>' will run the equivalent of nix run -f '<nixpkgs>' delve go gotools + some shell setup.

I see nix run for the first time, but from the manual page it would seem that no.

#5

nix-shell doesn’t build the package itself. I haven’t looked into the details but AIUI it doesn’t run the phases for the derivation, though I think it makes them available as shell functions. mkShell disables building by defining a single phase that always aborts. nix run does build the derivation and makes it available to the shell. It’s like nix-shell -p, which I would assume would abort on a mkShell the same way nix run does.

#6

tl;dr nix run is a “new interface”, part of nix 2.0. This new interface has no implementation for the feature from nix-shell that you are looking for.

nix run pkg1 pkg2 supersedes nix-shell -p pkg1 pkg2, but nix-shell -A pkg has no equivalent. The magic of nix-shell -A pkg is that it goes trough all the dependencies of pkg and starts a shell with these packages in the environment.

To reproduce that, you need to be able to extract all the dependencies of a package, which is done in nix-shell (around here and here) but has no equivalent in nix run (here) because it has no extraction logic.

If you want to emulate nix-shell with nix run, you may consider using nix run $(nix-build $(nix-store -q --references $(nix-instantiate) | grep ".drv$" | grep -v bash)) which still uses the old interface to obtain the .drv (nix-instantiate), to extract the dependencies (nix-store -q --references) and to build these .drvs (nix-build). The new nix build interface does not print the outputs, and therefore cannot be used here. the others have no equivalent.

nix 2.0 interface is still a work in progress, you should stick with nix-shell to get a shell from a derivation seen as an “environment” for that shell.

4 Likes
#7

Thanks for that explanation!

Is there the intention to implement something like nix shell in nix 2.x or is the plan to keep nix-shell around?

#8

That would be a question for @edolstra, but here is what seems likely to happen from my point of view, which may be very wrong.

nix-shell contains a lot of ad-hoc hacks to setup the environment, and this stuff does not really pertain to nix per se. So I guess there is little chance to see nix-shell ported as-is. More probably, the functionnality will see a large reactoring to move most of the custom hacks into nixpkgs and cleaner, well-defined interfaces.

This is a lot of work, and no-one seems too eager to start working on it as nix-shell is still fully supported, maintained, and working as intended.