mkShell: make it buildable (#153194)

When I designed `mkShell`, I didn't have a good idea of what the output
should look like and so decided to make the build fail. In practice,
this causes quite a bit of confusion and complications because now the
shell cannot be part of a normal package set without failing the CI as
well.

This commit changes that build phase to record all the build inputs in a
file. That way it becomes possible to build it, makes sure that all the
build inputs get built as well, and also can be used as a GC root.
(by applying the same trick as #95536).

The documentation has also been improved to better describe what mkShell
does and how to use it.

authored by zimbatm.tngl.sh and committed by GitHub 1e910209 cacab72b

+42 -17
+26 -6
doc/builders/special/mkshell.section.md
··· 1 1 # pkgs.mkShell {#sec-pkgs-mkShell} 2 2 3 - `pkgs.mkShell` is a special kind of derivation that is only useful when using 4 - it combined with `nix-shell`. It will in fact fail to instantiate when invoked 5 - with `nix-build`. 3 + `pkgs.mkShell` is a specialized `stdenv.mkDerivation` that removes some 4 + repetition when using it with `nix-shell` (or `nix develop`). 6 5 7 6 ## Usage {#sec-pkgs-mkShell-usage} 8 7 8 + Here is a common usage example: 9 + 9 10 ```nix 10 11 { pkgs ? import <nixpkgs> {} }: 11 12 pkgs.mkShell { 12 - # specify which packages to add to the shell environment 13 13 packages = [ pkgs.gnumake ]; 14 - # add all the dependencies, of the given packages, to the shell environment 15 - inputsFrom = with pkgs; [ hello gnutar ]; 14 + 15 + inputsFrom = [ pkgs.hello pkgs.gnutar ]; 16 + 17 + shellHook = '' 18 + export DEBUG=1 19 + ''; 16 20 } 17 21 ``` 22 + 23 + ## Attributes 24 + 25 + * `name` (default: `nix-shell`). Set the name of the derivation. 26 + * `packages` (default: `[]`). Add executable packages to the `nix-shell` environment. 27 + * `inputsFrom` (default: `[]`). Add build dependencies of the listed derivations to the `nix-shell` environment. 28 + * `shellHook` (default: `""`). Bash statements that are executed by `nix-shell`. 29 + 30 + ... all the attributes of `stdenv.mkDerivation`. 31 + 32 + ## Building the shell 33 + 34 + This derivation output will contain a text file that contains a reference to 35 + all the build inputs. This is useful in CI where we want to make sure that 36 + every derivation, and its dependencies, build properly. Or when creating a GC 37 + root so that the build dependencies don't get garbage-collected.
+16 -11
pkgs/build-support/mkshell/default.nix
··· 1 - { lib, stdenv }: 1 + { lib, stdenv, buildEnv }: 2 2 3 3 # A special kind of derivation that is only meant to be consumed by the 4 4 # nix-shell. 5 - { 6 - # a list of packages to add to the shell environment 5 + { name ? "nix-shell" 6 + , # a list of packages to add to the shell environment 7 7 packages ? [ ] 8 8 , # propagate all the inputs from the given derivations 9 9 inputsFrom ? [ ] ··· 15 15 }@attrs: 16 16 let 17 17 mergeInputs = name: 18 - (attrs.${name} or []) ++ 18 + (attrs.${name} or [ ]) ++ 19 19 (lib.subtractLists inputsFrom (lib.flatten (lib.catAttrs name inputsFrom))); 20 20 21 21 rest = builtins.removeAttrs attrs [ 22 + "name" 22 23 "packages" 23 24 "inputsFrom" 24 25 "buildInputs" ··· 30 31 in 31 32 32 33 stdenv.mkDerivation ({ 33 - name = "nix-shell"; 34 - phases = [ "nobuildPhase" ]; 34 + inherit name; 35 35 36 36 buildInputs = mergeInputs "buildInputs"; 37 37 nativeBuildInputs = packages ++ (mergeInputs "nativeBuildInputs"); ··· 41 41 shellHook = lib.concatStringsSep "\n" (lib.catAttrs "shellHook" 42 42 (lib.reverseList inputsFrom ++ [ attrs ])); 43 43 44 - nobuildPhase = '' 45 - echo 46 - echo "This derivation is not meant to be built, aborting"; 47 - echo 48 - exit 1 44 + phases = [ "buildPhase" ]; 45 + 46 + buildPhase = '' 47 + echo "------------------------------------------------------------" >>$out 48 + echo " WARNING: the existence of this path is not guaranteed." >>$out 49 + echo " It is an internal implementation detail for pkgs.mkShell." >>$out 50 + echo "------------------------------------------------------------" >>$out 51 + echo >> $out 52 + # Record all build inputs as runtime dependencies 53 + export >> $out 49 54 ''; 50 55 } // rest)