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.
I somehow accidentally left out the lib.flatten from mergeInputs. Without it, subtractLists won't ever remove anything from the inputs since the inputs will be a list of lists.
The motivation for inputsFrom is to create a shell environment that is suitable for development of the packages listed in inputsFrom. This commit filters out any dependencies from one package in inputsFrom to another when computing the shell environment's inputs. This supports the use case where several closely related packages (perhaps even built from the same source tree) are being mutually developed. It is assumed that the user will configure their environment to resolve dependencies between these mutually developed packages.
The distinction between the inputs doesn't really make sense in the
mkShell context. Technically speaking, we should be using the
nativeBuildInputs most of the time.
So in order to make this function more beginner-friendly, add "packages"
as an attribute, that maps to nativeBuildInputs.
This commit also updates all the uses in nixpkgs.
mergeInputs is now simply defined in terms of `concatLists` and
`catAttrs` instead of a more complicated `foldr`.
Note that the order of PATH has also changed. For example running the
following with nix-shell:
let
pkgs = import <nixpkgs> {};
shell1 = pkgs.mkShell {
buildInputs = [ pkgs.htop ];
};
shell2 = pkgs.mkShell {
buildInputs = [ pkgs.hello ];
};
shell3 = pkgs.mkShell {
inputsFrom = [ shell1 shell2 ];
buildInputs = [ pkgs.tree ];
};
in shell3
Results in the following PATH:
$ echo $PATH
...
/nix/store/yifq4bikf7m07160bpia7z48ciqddbfi-tree-1.8.0/bin:
/nix/store/vhxqk81234ivqw1a7j200a1c69k8mywi-htop-2.2.0/bin:
/nix/store/n9vm3m58y1n3rg3mlll17wanc9hln58k-hello-2.10/bin
...
Previously the order was:
/nix/store/n9vm3m58y1n3rg3mlll17wanc9hln58k-hello-2.10/bin
/nix/store/vhxqk81234ivqw1a7j200a1c69k8mywi-htop-2.2.0/bin:
/nix/store/yifq4bikf7m07160bpia7z48ciqddbfi-tree-1.8.0/bin:
I think the new order makes more sense because it allows to override
the PATH in the outermost mkShell.
Running the following expression with nix-shell:
let
pkgs = import <nixpkgs> {};
shell1 = pkgs.mkShell {
shellHook = ''
echo shell1
'';
};
shell2 = pkgs.mkShell {
shellHook = ''
echo shell2
'';
};
shell3 = pkgs.mkShell {
inputsFrom = [ shell1 shell2 ];
shellHook = ''
echo shell3
'';
};
in shell3
Will now results in:
shell2
shell1
shell3
Note that packages in the front of inputsFrom have precedence over
packages in the back. The outermost mkShell has precedence over all.