Development environment with nix-shell: Difference between revisions

imported>Bionicles
m fix extremely minor typo
Cdro (talk | contribs)
m Correct a typo
 
(18 intermediate revisions by 13 users not shown)
Line 3: Line 3:
If you already have a nix package definition of your project it's easy: Just use <code>nix-shell</code> instead of <code>nix-build</code> and you will end up in a bash shell that reproduce the build-environment of your package. You can also override[https://nixos.org/nixpkgs/manual/#sec-pkg-override] your package in a <code>shell.nix</code> 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.
If you already have a nix package definition of your project it's easy: Just use <code>nix-shell</code> instead of <code>nix-build</code> and you will end up in a bash shell that reproduce the build-environment of your package. You can also override[https://nixos.org/nixpkgs/manual/#sec-pkg-override] your package in a <code>shell.nix</code> 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 <code>shell.nix</code> file at the root of your repository. For example, if you want to have Ruby 2.3 and not one provided by your system you can write:
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 <code>shell.nix</code> file at the root of your repository. For example, if you want to have rustc with libraries and hello you can write:
<syntaxHighlight lang=nix>
<syntaxhighlight lang="nix">
{ pkgs ? import <nixpkgs> {} }:
{
  pkgs.mkShell {
  pkgs ? import <nixpkgs> { },
     # nativeBuildInputs is usually what you want -- tools you need to run
}:
     nativeBuildInputs = [ pkgs.buildPackages.ruby_2_3 ];
pkgs.callPackage (
}
  {
</syntaxHighlight>
    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
    ];
  }
) { }
 
</syntaxhighlight>


Then just run:
Then just run:
Line 18: Line 42:
{{Commands|$ nix-shell shell.nix}}
{{Commands|$ nix-shell shell.nix}}


Now you got ruby 2.3 in available in your shell:
Now you have rustc available in your shell:
<syntaxHighlight lang=bash>
<syntaxhighlight lang="bash">
$ ruby --version
$ rustc --version
ruby 2.3.7p456 (2018-03-28) [x86_64-linux]
rustc 1.80.1 (3f5fd8dd4 2024-08-06) (built from a source tarball)
</syntaxHighlight>
</syntaxhighlight>


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 <code>--pure</code> option.
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 <code>--pure</code> 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:
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:
<syntaxHighlight lang=nix>
<syntaxhighlight lang="nix">
{ pkgs ? import <nixpkgs> {} }:
{
pkgs.mkShell {
  pkgs ? import <nixpkgs> {
  # buildInputs is for dependencies you'd need "at run time",
    overlays = [
   # were you to to use nix-build not nix-shell and build whatever you were working on
      (final: prev: {
   buildInputs = [
        my-package = prev.callPackage ./my-package.nix { };
    (import ./my-expression.nix { inherit pkgs; })
      })
   ];
    ];
}
  },
</syntaxHighlight>
}:
pkgs.callPackage (
  {
    mkShell,
    my-package
   }:
   mkShell {
    strictDeps = true;
    buildInputs = [
      my-package
    ];
   }
) { }
</syntaxhighlight>


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


=== Small cross env note ===
== nix develop ==
 
For [[Flakes]]-based projects (<code>flake.nix</code> file in project root),
we replace <code>nix-shell</code> with <code>nix develop</code>
 
Example: Building Nix in a development shell, to get [[Incremental builds]] = faster recompiles. This is because Nix evaluations are cached.
 
<pre>
git clone https://github.com/NixOS/nix --depth 1
cd nix
nix develop
</pre>
 
Now what? Let's read the manual:
 
<pre>
less README.md
less doc/manual/src/contributing/hacking.md
</pre>
 
The contributing guide for Nix says:
 
<pre>
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
```
</pre>
 
So, in our <code>nix develop</code> shell, we run
 
<pre>
./bootstrap.sh
./configure $configureFlags --prefix=$(pwd)/outputs/out
make -j $NIX_BUILD_CORES
</pre>
 
This will compile Nix to <code>./outputs/out/bin/nix</code>
 
Let's make some changes to the source code, and run <code>make</code> again.<br>
Now the compilation should be much faster (see [[Incremental builds]])


The comments in the code snippets on <code>nativeBuildInputs</code> and <code>buildInputs</code> above might seem pendantic --- 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:
== stdenv.mkDerivation ==
<syntaxHighlight lang=nix>
{ pkgs ? import <nixpkgs> { crossSystem.config = "exotic_arch-unknown-exotic_os"; } }:
</syntaxHighlight>
and <code>nativeBuildInputs</code> would be for the native platform, while <code>buildInputs</code> 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!


== Using Direnv ==
Let's assume you have a <code>default.nix</code> file


One of the limitations of nix-shell is that you can't use a shell other than bash. Thankfully, there is Direnv [[https://direnv.net/]] with the support of Nix[[https://github.com/direnv/direnv/wiki/Nix]] to overcome this limitation. Also, Direnv provides some nice features like loading the environment automatically when you enter your project directory and show the loaded variables to you (explicit is always better;-)).
<syntaxhighlight lang="nix">
{ 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, ...
}
</syntaxhighlight>
 
Then you can start a development shell with


First, install Direnv:
<syntaxHighlight lang=bash>
<syntaxHighlight lang=bash>
nix-env -i direnv
nix-shell -E 'with import <nixpkgs> { }; callPackage ./default.nix { }'
</syntaxHighlight>
</syntaxHighlight>


Now, you need to add one more file in the root of your repository named <code>.envrc</code> that contains only this:
In this shell, you can run the phases of stdenv.mkDerivation:
 
<syntaxHighlight lang=bash>
<syntaxHighlight lang=bash>
use_nix
# clean build: copy sources from /nix/store
</syntaxHighlight>
echo "src = $src" && cd $(mktemp -d) && unpackPhase && cd *


Then depending on the shell you are using, you need to add a line in your configuration file. See the ''Setup'' section of the doc[https://direnv.net/]. For example, for Zsh put in your <code>~/.zshrc.local</code>:
# dirty build: keep cache files from last buildPhase, to compile faster
<syntaxHighlight lang=bash>
# this is useful to make many small changes to a large project
eval "$(direnv hook zsh)"
# after each change, just run `buildPhase`
</syntaxHighlight>
#cd $HOME/path/to/project


Then, still at the root of your repository, run:
configurePhase
<syntaxHighlight lang=bash>
$ direnv allow .
direnv: loading .envrc
direnv: using nix
[...]
+SIZE +SOURCE_DATE_EPOCH +STRINGS +STRIP +TEMP +TEMPDIR +TMP +TMPDIR +_PATH +buildInputs +builder +checkPhase +cmakeFlags +configureFlags +doCheck +enableParallelBuilding +name +nativeBuildInputs +out +postCheck +preCheck +preConfigure +propagatedBuildInputs +propagatedNativeBuildInputs +shell +src +stdenv +system +testInputs +version ~PATH
</syntaxHighlight>


Bonus: you can see all the variables set by the nix-shell :)
buildPhase # most time is spent here


Now you can leave your project and the environment will be unloaded:
checkPhase && installPhase && fixupPhase
<syntaxHighlight lang=bash>
$ cd ..
direnv: unloading
</syntaxHighlight>
</syntaxHighlight>


No need to use <code>direnv allow</code> anymore, the next time you go to your project the environment will be loaded!
== cross env ==


More explanation and configuration tweaks can be found in the Direnv wiki [https://github.com/direnv/direnv/wiki/Nix].
The comments in the code snippets on <code>nativeBuildInputs</code> and <code>buildInputs</code> 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:
<syntaxHighlight lang=nix>
{ pkgs ? import <nixpkgs> { crossSystem.config = "exotic_arch-unknown-exotic_os"; } }:
</syntaxHighlight>
and <code>nativeBuildInputs</code> would be for the native platform, while <code>buildInputs</code> 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 ==
== Troubleshooting ==
Line 105: Line 193:
For packages we use <code>wrapGAppsHook</code> in <code>nativeBuildInputs</code>, however in nix-shell this is not working as expected.
For packages we use <code>wrapGAppsHook</code> in <code>nativeBuildInputs</code>, 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 <code>mkShell</code> expression:
To get your application to work in nix-shell you will need to add the following to your <code>mkShell</code> expression:
<syntaxHighlight lang=nix>
<syntaxhighlight lang="nix">
mkShell {
mkShell {
   ...
   ...
  strictDeps = true;
   buildInputs = [ gtk3 ];
   buildInputs = [ gtk3 ];
   shellHook = ''
   shellHook = ''
     XDG_DATA_DIRS=$GSETTINGS_SCHEMA_PATH
     export XDG_DATA_DIRS=$GSETTINGS_SCHEMAS_PATH
   '';
   '';
}
}
</syntaxHighlight>
</syntaxhighlight>


This may also called: <code>$GSETTINGS_SCHEMA'''S'''_PATH</code>.
This may also called: <code>$GSETTINGS_SCHEMA'''S'''_PATH</code>.
Line 120: Line 209:
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>
<pre> XDG_DATA_DIRS=...:${hicolor-icon-theme}/share:${gnome3.adwaita-icon-theme}/share</pre>
== See Also ==
* [[Direnv]]
* [[Command Shell#Using a different shell in nix-shell and nix develop]]
[[Category:Development]]
[[Category:nix]]