Development environment with nix-shell: Difference between revisions
imported>Mickours Add shell.nix + direnv setup |
m Correct a typo |
||
(31 intermediate revisions by 23 users not shown) | |||
Line 1: | Line 1: | ||
Nix can be used to | Nix can be used to provide some kind of '''virtual environment''' through the <code>nix-shell</code> command. | ||
If you already have a nix package definition of | 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 root of your repository. For example, if you want to have | 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"> | ||
{ pkgs ? import <nixpkgs> {} }: | { | ||
pkgs ? import <nixpkgs> { }, | |||
buildInputs = [ | }: | ||
} | 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 | |||
]; | |||
} | |||
) { } | |||
</syntaxhighlight> | |||
Then just run: | Then just run: | ||
Line 17: | Line 42: | ||
{{Commands|$ nix-shell shell.nix}} | {{Commands|$ nix-shell shell.nix}} | ||
Now you | Now you have rustc available in your shell: | ||
< | <syntaxhighlight lang="bash"> | ||
$ | $ rustc --version | ||
rustc 1.80.1 (3f5fd8dd4 2024-08-06) (built from a source tarball) | |||
</ | </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, see comments in the earlier example for where to put your package: | ||
<syntaxhighlight lang="nix"> | |||
{ | |||
pkgs ? import <nixpkgs> { | |||
overlays = [ | |||
(final: prev: { | |||
my-package = prev.callPackage ./my-package.nix { }; | |||
}) | |||
]; | |||
}, | |||
}: | |||
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]]. | |||
== 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]]) | |||
== stdenv.mkDerivation == | |||
Let's assume you have a <code>default.nix</code> file | |||
<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 | |||
<syntaxHighlight lang=bash> | <syntaxHighlight lang=bash> | ||
nix- | nix-shell -E 'with import <nixpkgs> { }; callPackage ./default.nix { }' | ||
</syntaxHighlight> | </syntaxHighlight> | ||
In this shell, you can run the phases of stdenv.mkDerivation: | |||
<syntaxHighlight lang=bash> | <syntaxHighlight lang=bash> | ||
# 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 | |||
</syntaxHighlight> | </syntaxHighlight> | ||
== cross env == | |||
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 == | |||
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=bash> | ||
= note: impure path `/[...]' used in link | |||
</syntaxHighlight> | </syntaxHighlight> | ||
This happens due to a specialty in nix: <code>ld</code> 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 | |||
<syntaxHighlight lang=bash> | <syntaxHighlight lang=bash> | ||
NIX_ENFORCE_PURITY=0 | |||
</syntaxHighlight> | </syntaxHighlight> | ||
in the nix-shell. | |||
=== No GSettings schemas are installed on the system === | |||
When working with gtk, the <code>XDG_DATA_DIRS</code> must contain a path to the gtk schemas, if not an application may crash with the error above. | |||
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: | |||
<syntaxhighlight lang="nix"> | |||
mkShell { | |||
... | |||
strictDeps = true; | |||
buildInputs = [ gtk3 ]; | |||
shellHook = '' | |||
export XDG_DATA_DIRS=$GSETTINGS_SCHEMAS_PATH | |||
''; | |||
} | |||
</syntaxhighlight> | |||
This may also called: <code>$GSETTINGS_SCHEMA'''S'''_PATH</code>. | |||
=== Icons not working === | |||
< | 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> | |||
</ | == See Also == | ||
* [[Direnv]] | |||
* [[Command Shell#Using a different shell in nix-shell and nix develop]] | |||
[[Category:Development]] | |||
[[Category:nix]] |