Development environment with nix-shell: Difference between revisions
imported>Bensleveritt m Fixes minor typos, and edits for readablilty |
m Correct a typo |
||
(25 intermediate revisions by 17 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 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 | 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: | 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> {} }: | { | ||
pkgs.mkShell { | 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 == | == Troubleshooting == | ||
Line 92: | 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"> | ||
mkShell { | mkShell { | ||
... | ... | ||
strictDeps = true; | |||
buildInputs = [ gtk3 ]; | buildInputs = [ gtk3 ]; | ||
shellHook = '' | shellHook = '' | ||
XDG_DATA_DIRS=$ | export XDG_DATA_DIRS=$GSETTINGS_SCHEMAS_PATH | ||
''; | ''; | ||
} | } | ||
</ | </syntaxhighlight> | ||
This may also called: <code>$GSETTINGS_SCHEMA'''S'''_PATH</code>. | |||
=== 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> | <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]] |