Difference between nix-shell and "nix run"?

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:.

5 Likes

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

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?

1 Like

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.

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.

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.

10 Likes

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?

1 Like

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.

More than 10 months passed since the last reply here - are there any news regarding nix-shell in the new interface? Maybe there is some ticket on the topic?

3 Likes

Going to quote @lilyball’s reply verbatim from another thread because it is highly relevant:

BTW the “better option” here is nix run . nix-shell -p foo sets up the environment as it would for building foo , so all of foo 's dependencies are in your PATH and all of the other environment vars are set too (I count 23 env vars containing NIX ), but nix run produces an environment much more similar to what you’d get if you simply installed the package (e.g. it basically just prefixes your PATH with the appropriate directories from the specified package).

That said, nix-shell replaces your normal shell initialization files and nix run doesn’t, so depending on how your .bashrc is set up you might override the PATH . For example with my setup, echo $PATH in a nix run shell starts the path with /usr/local/bin before the nix-provided path value, because my current .bashrc unconditionally prefixes my PATH (which I really should fix).

2 Likes

If I have a shell.nix file, I can simply run

❯ nix-shell
these paths will be fetched (0.05 MiB download, 0.28 MiB unpacked):
  /nix/store/qgbwdnk91rk26b5bkd6qv5r6c2v733kb-bash-interactive-4.4-p23-dev
copying path '/nix/store/qgbwdnk91rk26b5bkd6qv5r6c2v733kb-bash-interactive-4.4-p23-dev' from 'https://cache.nixos.org'...

[nix-shell:~/dev/research/julia/odecontrol]$

Can I do something similar with nix run? I haven’t been able to make it work:

❯ nix run
error: opening file '/home/skainswo/dev/research/julia/odecontrol/default.nix': No such file or directory

nix 2.3 or 2.4pre?

For 2.3, no, run does not read from a shell.nix, as the content of a shell.nix is not meant to be built. run though builds and then gives the build result in the environment. nix-shell and nix shell (2.3) though do not build the mentioned derivations, but instead their buildinputs and give those into the environment.

2.4s run works completely different from both, and require a flake with applications.

3 Likes

nix develop is more similar to that usage, you can also use nix shell, but nix shell is more similar to nix-shell -p

1 Like

What is the difference between nix shell and nix develop? From nix --help for 2.4pre:

nix develop - run a bash shell that provides the build environment of a derivation

nix shell - run a shell in which the specified packages are available

but I still can’t mentally differentiate between the two, unlike nix run, which clearly has a different purpose now - to run nix apps.

2 Likes

nix develop takes a single derivation, and gives you a shell to make it (all dependencies needed to build it will be exposed in the shell).

nix shell, essentially adds packages to the current shell. (Technically forks the shell, and in the child shell will add the packages)

Example:

nix develop nixpkgs#ripgrep → You will have rust, pkg-config, and a few other packages added to your shell, but not ripgrep

nix shell nixpkgs#ripgrep → You will have access to ripgrep, and nothing else will be added.

5 Likes

not really the current shell.

I think a better definition would be:
nix shell will start a new shell where all the packages you give it are available to use.

So nix develop is analogous to nix-build while nix shell is analogous to nix-shell.

But then there is also nix build.

Maybe it’s futile to draw similarities between the old and the new commands, just take them for what they are.

@jayesh.bhoot I think a better breakdown would look like this:

use old command new command
start a new shell that has ripgrep available nix-shell -p ripgrep nix shell nixpkgs#ripgrep
start a new shell that has the dependencies required for building ripgrep available (like gcc and rustc) nix-shell -A ripgrep nix develop nixpkgs#ripgrep
build ripgrep completely within Nix. Put the resulting binary in a result/ symlink nix-build -A ripgrep nix build nixpkgs#ripgrep
run an application defined in a flake nothing similar? nix run nixpkgs#ripgrep

A couple things to keep in mind:

  • nix build, nix develop, and nix run have special support for flakes. For instance, if there is a flake.nix in the current directory, then:

    • nix build with no arguments will try to build the outputs.defaultPackage.
    • nix develop with no arguments will load the shell defined in outputs.devShell.
    • nix run with no arugments will run the command defined in outputs.defaultApp.
  • You might be able to make the argument that nix run is similar to nix-shell -p ripgrep --run 'ripgrep', but this is somewhat of a stretch.

  • nix shell may be renamed: Rename 'nix shell' · Issue #4715 · NixOS/nix · GitHub

11 Likes

Thank you. This, combined with Flakes - NixOS Wiki, give us a lot of clarity.

The most authoritative page on the nix command seems to be Nix command - NixOS Wiki. You should consider putting your response there.

1 Like

While the wiki does have a lot of good info, I’d recommend you make sure to read through all the man pages for nix. By now I think these pages have a lot more info on the new-style Nix commands:

https://nixos.org/manual/nix/unstable/command-ref/experimental-commands.html

1 Like