nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1{
2 lib,
3 runtimeShell,
4 bashInteractive,
5 stdenv,
6 writeTextFile,
7}:
8let
9 inherit (builtins) typeOf;
10in
11rec {
12 # Docs: doc/build-helpers/dev-shell-tools.chapter.md
13 # Tests: ./tests/default.nix
14 # This function closely mirrors what this Nix code does:
15 # https://github.com/NixOS/nix/blob/2.8.0/src/libexpr/primops.cc#L1102
16 # https://github.com/NixOS/nix/blob/2.8.0/src/libexpr/eval.cc#L1981-L2036
17 valueToString =
18 value:
19 # We can't just use `toString` on all derivation attributes because that
20 # would not put path literals in the closure. So we explicitly copy
21 # those into the store here
22 if typeOf value == "path" then
23 "${value}"
24 else if typeOf value == "list" then
25 toString (map valueToString value)
26 else
27 toString value;
28
29 # Docs: doc/build-helpers/dev-shell-tools.chapter.md
30 # Tests: ./tests/default.nix
31 # https://github.com/NixOS/nix/blob/2.8.0/src/libstore/build/local-derivation-goal.cc#L992-L1004
32 unstructuredDerivationInputEnv =
33 { drvAttrs }:
34 # FIXME: this should be `normalAttrs // passAsFileAttrs`
35 lib.mapAttrs'
36 (
37 name: value:
38 let
39 str = valueToString value;
40 in
41 if lib.elem name (drvAttrs.passAsFile or [ ]) then
42 let
43 nameHash =
44 if builtins ? convertHash then
45 builtins.convertHash {
46 hash = "sha256:" + builtins.hashString "sha256" name;
47 toHashFormat = "nix32";
48 }
49 else
50 builtins.hashString "sha256" name;
51 basename = ".attr-${nameHash}";
52 in
53 lib.nameValuePair "${name}Path" "${
54 writeTextFile {
55 name = "shell-passAsFile-${name}";
56 text = str;
57 destination = "/${basename}";
58 }
59 }/${basename}"
60 else
61 lib.nameValuePair name str
62 )
63 (
64 removeAttrs drvAttrs [
65 # TODO: there may be more of these
66 "args"
67 ]
68 );
69
70 # Docs: doc/build-helpers/dev-shell-tools.chapter.md
71 # Tests: ./tests/default.nix
72 derivationOutputEnv =
73 { outputList, outputMap }:
74 # A mapping from output name to the nix store path where they should end up
75 # https://github.com/NixOS/nix/blob/2.8.0/src/libexpr/primops.cc#L1253
76 lib.genAttrs outputList (output: builtins.unsafeDiscardStringContext outputMap.${output}.outPath);
77
78 toBashEnv =
79 { env }:
80 lib.concatStringsSep "\n" (
81 lib.mapAttrsToList (
82 name: value:
83 if lib.isValidPosixName name then ''export ${name}=${lib.escapeShellArg value}'' else ""
84 ) env
85 );
86
87 buildShellEnv =
88 {
89 drvAttrs,
90 promptPrefix ? "build shell",
91 promptName ? null,
92 }:
93 let
94 name = drvAttrs.pname or drvAttrs.name or "shell";
95 env = unstructuredDerivationInputEnv { inherit drvAttrs; };
96 in
97 stdenv.mkDerivation (finalAttrs: {
98 name = "${name}-env";
99 passAsFile = [
100 "bashEnv"
101 "bashrc"
102 "runShell"
103 ];
104 bashEnv = toBashEnv { inherit env; };
105 bashrc = ''
106 export NIXPKGS_SHELL_TMP="$(mktemp -d --tmpdir nixpkgs-shell-${name}.XXXXXX)"
107 export TMPDIR="$NIXPKGS_SHELL_TMP"
108 export TEMPDIR="$NIXPKGS_SHELL_TMP"
109 export TMP="$NIXPKGS_SHELL_TMP"
110 export TEMP="$NIXPKGS_SHELL_TMP"
111
112 echo "Using TMPDIR=$TMPDIR"
113
114 source @envbash@
115
116 mkdir -p $TMP/outputs
117 for _output in $outputs; do
118 export "''${_output}=$TMP/outputs/''${_output}"
119 done
120
121 source @stdenv@/setup
122
123 # Set a distinct prompt to make it clear that we are in a build shell
124 case "$PS1" in
125 *"(build shell $name)"*)
126 echo "It looks like your running a build shell inside a build shell."
127 echo "It might work, but this is probably not what you want."
128 echo "You may want to exit your shell before loading a new one."
129 ;;
130 esac
131
132 # Prefix a line to the prompt to indicate that we are in a build shell
133 PS1=$"\n(\[\033[1;33m\]"${lib.escapeShellArg promptPrefix}$": \[\033[1;34m\]"${
134 if promptName != null then lib.escapeShellArg promptName else ''"$name"''
135 }"\[\033[1;33m\]\[\033[0m\]) $PS1"
136
137 runHook shellHook
138 '';
139 buildCommand = ''
140 mkdir -p $out/lib $out/bin
141 bashrc="$out/lib/bashrc"
142 envbash="$out/lib/env.bash"
143
144 mv "$bashEnvPath" "$envbash"
145 substitute "$bashrcPath" "$bashrc" \
146 --replace-fail "@envbash@" "$envbash" \
147 --replace-fail "@stdenv@" "$stdenv" \
148 ;
149
150 substitute ${./run-shell.sh} "$out/bin/run-shell" \
151 --replace-fail "@bashrc@" "$bashrc" \
152 --replace-fail "@runtimeShell@" "${runtimeShell}" \
153 --replace-fail "@bashInteractive@" "${bashInteractive}" \
154 ;
155
156 # NOTE: most other files are script for the source command, and not
157 # standalone executables, so they should not be made executable.
158 chmod a+x $out/bin/run-shell
159 '';
160 preferLocalBuild = true;
161 passthru = {
162 # Work this as a shell environment, so that commands like `nix-shell`
163 # will be able to check it and use it correctly. This also lets Nix know
164 # to stop when the user requests pkg.devShell explicitly, or a different
165 # attribute containing a shell environment.
166 isShellEnv = true;
167 devShell = throw "You're trying to access the devShell attribute of a shell environment. We appreciate that this is very \"meta\" and interesting, but it's usually just not what you want. Most likely you've selected one `.devShell` to deep in an expression or on the command line. Try removing the last one.";
168 };
169 meta = {
170 description = "An environment similar to the build environment of ${name}";
171 # TODO longDescription
172 mainProgram = "run-shell";
173 };
174 });
175}