Jump to content

Development environment with nix-shell: Difference between revisions

From Official NixOS Wiki
Mightyiam (talk | contribs)
Link to relevant Nixcademy article
DHCP (talk | contribs)
m style improvements
 
Line 37: Line 37:


Then just run:
Then just run:
{{Commands|$ nix-shell}}
 
<syntaxhighlight lang="console">
$ nix-shell
</syntaxhighlight>


Or, to be more explicit:
Or, to be more explicit:
{{Commands|$ nix-shell shell.nix}}
 
<syntaxhighlight lang="console">
$ nix-shell shell.nix
</syntaxhighlight>


Now you have rustc available in your shell:
Now you have rustc available in your shell:
<syntaxhighlight lang="bash">
<syntaxhighlight lang="console">
$ rustc --version
$ rustc --version
rustc 1.80.1 (3f5fd8dd4 2024-08-06) (built from a source tarball)
rustc 1.80.1 (3f5fd8dd4 2024-08-06) (built from a source tarball)
Line 84: Line 90:
Example: Building Nix in a development shell, to get [[Incremental builds]] = faster recompiles. This is because Nix evaluations are cached.
Example: Building Nix in a development shell, to get [[Incremental builds]] = faster recompiles. This is because Nix evaluations are cached.


<pre>
<syntaxhighlight lang="console">
git clone https://github.com/NixOS/nix --depth 1
$ git clone https://github.com/NixOS/nix --depth 1
cd nix
$ cd nix
nix develop
$ nix develop
</pre>
</syntaxhighlight>


Now what? Let's read the manual:
Now what? Let's read the manual:


<pre>
<syntaxhighlight lang="console">
less README.md
$ less README.md
less doc/manual/src/contributing/hacking.md
$ less doc/manual/src/contributing/hacking.md
</pre>
</syntaxhighlight>


The contributing guide for Nix says:
The contributing guide for Nix says:
Line 118: Line 124:
So, in our <code>nix develop</code> shell, we run
So, in our <code>nix develop</code> shell, we run


<pre>
<syntaxhighlight lang="console">
./bootstrap.sh
$ ./bootstrap.sh
./configure $configureFlags --prefix=$(pwd)/outputs/out
$ ./configure $configureFlags --prefix=$(pwd)/outputs/out
make -j $NIX_BUILD_CORES
$ make -j $NIX_BUILD_CORES
</pre>
</syntaxhighlight>


This will compile Nix to <code>./outputs/out/bin/nix</code>
This will compile Nix to <code>./outputs/out/bin/nix</code>
Line 146: Line 152:
Then you can start a development shell with
Then you can start a development shell with


<syntaxHighlight lang=bash>
<syntaxHighlight lang=console>
nix-shell -E 'with import <nixpkgs> { }; callPackage ./default.nix { }'
$ nix-shell -E 'with import <nixpkgs> { }; callPackage ./default.nix { }'
</syntaxHighlight>
</syntaxHighlight>


Line 159: Line 165:
# this is useful to make many small changes to a large project
# this is useful to make many small changes to a large project
# after each change, just run `buildPhase`
# after each change, just run `buildPhase`
#cd $HOME/path/to/project
# cd $HOME/path/to/project


configurePhase
configurePhase
Line 179: Line 185:


When compiling software which links against local files (e.g. when compiling with rust's cargo), you may encounter the following problem:
When compiling software which links against local files (e.g. when compiling with rust's cargo), you may encounter the following problem:
<syntaxHighlight lang=bash>
<syntaxHighlight lang=console>
= note: impure path `/[...]' used in link
= note: impure path `/[...]' used in link
</syntaxHighlight>
</syntaxHighlight>
Line 208: Line 214:
=== Icons not working ===
=== Icons not working ===
Similar to the Gsettings issue, icons can be added with XDG_DATA_DIRS:
Similar to the Gsettings issue, icons can be added with XDG_DATA_DIRS:
<pre> XDG_DATA_DIRS=...:${hicolor-icon-theme}/share:${gnome3.adwaita-icon-theme}/share</pre>
<syntaxhighlight lang=bash>
XDG_DATA_DIRS=...:${hicolor-icon-theme}/share:${gnome3.adwaita-icon-theme}/share
</syntaxhighlight>


== See Also ==
== See Also ==

Latest revision as of 12:43, 1 June 2026

Nix can be used to provide some kind of virtual environment through the nix-shell command.

If you already have a nix package definition of your project it's easy: Just use nix-shell instead of nix-build and you will end up in a bash shell that reproduce the build-environment of your package. You can also override[1] your package in a shell.nix file to add test and coverage dependencies, that are not necessary for the actual build of the package, but that you want for your development environment.

But, if you don't (or you don't want to) have a package definition you can still use a nix-shell to provide a reproducible development environment. To do so, you have to create a shell.nix file at the root of your repository. For example, if you want to have rustc with libraries and hello you can write:

{
  pkgs ? import <nixpkgs> { },
}:
pkgs.callPackage (
  {
    mkShell,
    hello,
    rustc,
    pkg-config,
    openssl,
  }:
  mkShell {
    strictDeps = true;
    # host/target agnostic programs
    depsBuildBuild = [
      hello
    ];
    # compilers & linkers & dependency finding programs
    nativeBuildInputs = [
      rustc
      pkg-config
    ];
    # libraries
    buildInputs = [
      openssl
    ];
  }
) { }

Then just run:

$ nix-shell

Or, to be more explicit:

$ nix-shell shell.nix

Now you have rustc available in your shell:

$ rustc --version
rustc 1.80.1 (3f5fd8dd4 2024-08-06) (built from a source tarball)

To be sure that the tools installed on your system will not interfere with the dependencies that you've defined in the shell you can use the --pure option.

If you'd like to load a local nix expression into a shell you can do it by modifying the earlier example a little bit, see comments in the earlier example for where to put your package:

{
  pkgs ? import <nixpkgs> {
    overlays = [
      (final: prev: {
        my-package = prev.callPackage ./my-package.nix { };
      })
    ];
  },
}:
pkgs.callPackage (
  {
    mkShell,
    my-package
  }:
  mkShell {
    strictDeps = true;
    buildInputs = [
      my-package
    ];
  }
) { }

If you want to see how to manually run the various phases of a given derivation from a nix-shell (useful to debug), see Nixpkgs/Create_and_debug_packages#Using_nix-shell_for_package_development.

nix develop

For Flakes-based projects (flake.nix file in project root), we replace nix-shell with nix develop

Example: Building Nix in a development shell, to get Incremental builds = faster recompiles. This is because Nix evaluations are cached.

$ git clone https://github.com/NixOS/nix --depth 1
$ cd nix
$ nix develop

Now what? Let's read the manual:

$ less README.md
$ less doc/manual/src/contributing/hacking.md

The contributing guide for Nix says:

To build all dependencies and start a shell in which all environment
variables are set up so that those dependencies can be found:

```console
$ nix-shell
```

To build Nix itself in this shell:

```console
[nix-shell]$ ./bootstrap.sh
[nix-shell]$ ./configure $configureFlags --prefix=$(pwd)/outputs/out
[nix-shell]$ make -j $NIX_BUILD_CORES
```

So, in our nix develop shell, we run

$ ./bootstrap.sh
$ ./configure $configureFlags --prefix=$(pwd)/outputs/out
$ make -j $NIX_BUILD_CORES

This will compile Nix to ./outputs/out/bin/nix

Let's make some changes to the source code, and run make again.
Now the compilation should be much faster (see Incremental builds)

stdenv.mkDerivation

Let's assume you have a default.nix file

{ stdenv, python }:
stdenv.mkDerivation {
  pname = "some-package";
  strictDeps = true;
  nativeBuildInputs = [ python ];
  version = "0.0.1";
  src = /home/yourname/path/to/project; # can be a local path, or fetchFromGitHub, fetchgit, ...
}

Then you can start a development shell with

$ nix-shell -E 'with import <nixpkgs> { }; callPackage ./default.nix { }'

In this shell, you can run the phases of stdenv.mkDerivation:

# clean build: copy sources from /nix/store
echo "src = $src" && cd $(mktemp -d) && unpackPhase && cd *

# dirty build: keep cache files from last buildPhase, to compile faster
# this is useful to make many small changes to a large project
# after each change, just run `buildPhase`
# cd $HOME/path/to/project

configurePhase

buildPhase # most time is spent here

checkPhase && installPhase && fixupPhase

cross env

The comments in the code snippets on nativeBuildInputs and buildInputs above might seem pedantic --- who cares about build-time vs run-time when we're just making a dev environment, not a real package! However, the distinction becomes of practical importance if one wants a cross compilation development environment. In that case one would begin file with something like:

{ pkgs ? import <nixpkgs> { crossSystem.config = "exotic_arch-unknown-exotic_os"; } }:

and nativeBuildInputs would be for the native platform, while buildInputs would be for the foreign platform. That's a much more practical distinction: any tool that's miscategorized one won't be able to run, and any library that's miscategorized one won't be able to link!

Troubleshooting

When compiling software which links against local files (e.g. when compiling with rust's cargo), you may encounter the following problem:

= note: impure path `/[...]' used in link

This happens due to a specialty in nix: ld is wrapped in a shell script which refuses to link against files not residing in the nix store, to ensure the purity of builds. Obviously this is not useful when building locally, for example in your home directory. To disable this behavior simply set

NIX_ENFORCE_PURITY=0

in the nix-shell.

No GSettings schemas are installed on the system

When working with gtk, the XDG_DATA_DIRS must contain a path to the gtk schemas, if not an application may crash with the error above.

For packages we use wrapGAppsHook in nativeBuildInputs, however in nix-shell this is not working as expected. To get your application to work in nix-shell you will need to add the following to your mkShell expression:

mkShell {
  ...
  strictDeps = true;
  buildInputs = [ gtk3 ];
  shellHook = ''
     export XDG_DATA_DIRS=$GSETTINGS_SCHEMAS_PATH
  '';
}

This may also called: $GSETTINGS_SCHEMAS_PATH.

Icons not working

Similar to the Gsettings issue, icons can be added with XDG_DATA_DIRS:

XDG_DATA_DIRS=...:${hicolor-icon-theme}/share:${gnome3.adwaita-icon-theme}/share

See Also