Skip to content

Commit 1fa235b

Browse files
roberthEricson2314
authored andcommitted
devShells: Infer inputs from input closure boundary
1 parent e2040ae commit 1fa235b

File tree

1 file changed

+203
-25
lines changed

1 file changed

+203
-25
lines changed

packaging/dev-shell.nix

Lines changed: 203 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,118 @@
33
devFlake,
44
}:
55

6+
let
7+
# Some helper functions
8+
9+
/**
10+
Compute a filtered closure of build inputs.
11+
12+
Specifically, `buildInputsClosure cond startSet` computes the closure formed
13+
by recursive application of `p: filter cond p.buildInputs ++ filter cond p.propagatedBuildInputs`
14+
to `startSet`.
15+
16+
Example:
17+
```nix
18+
builtInputsClosure isInternal [ pkg1 pkg2 ]
19+
=> [ pkg1 pkg3 pkg2 pkg10 ]
20+
```
21+
22+
Note: order tbd
23+
24+
Note: `startSet` is *NOT* filtered.
25+
*/
26+
buildInputsClosureCond =
27+
cond: startSet:
28+
let
29+
closure = builtins.genericClosure {
30+
startSet = map (d: {
31+
key = d.drvPath;
32+
value = d;
33+
}) startSet;
34+
operator =
35+
d:
36+
let
37+
r =
38+
map
39+
(d': {
40+
key = d'.drvPath;
41+
value = d';
42+
})
43+
(
44+
lib.filter cond d.value.buildInputs or [ ] ++ lib.filter cond d.value.propagatedBuildInputs or [ ]
45+
);
46+
in
47+
r;
48+
};
49+
in
50+
map (item: item.value) closure;
51+
52+
/**
53+
`[ pkg1 pkg2 ]` -> `{ "...-pkg2.drv" = null; "...-pkg1.drv" = null }`
54+
55+
Note: fairly arbitrary order (hash based). Use for efficient set membership test only.
56+
*/
57+
byDrvPath =
58+
l:
59+
lib.listToAttrs (
60+
map (c: {
61+
name =
62+
# Just a lookup key
63+
builtins.unsafeDiscardStringContext c.drvPath;
64+
value = null;
65+
}) l
66+
);
67+
68+
/**
69+
Stable dedup.
70+
71+
Unlike `listToAttrs` -> `attrValues`, this preserves the input ordering,
72+
which is more predictable ("deterministic") than e.g. sorting store paths,
73+
whose hashes affect the ordering on every change.
74+
*/
75+
# TODO: add to Nixpkgs lib, refer from uniqueStrings
76+
dedupByString =
77+
key: l:
78+
let
79+
r =
80+
lib.foldl'
81+
(
82+
a@{ list, set }:
83+
elem:
84+
let
85+
k = builtins.unsafeDiscardStringContext (key elem);
86+
in
87+
if set ? ${k} then
88+
a
89+
else
90+
let
91+
# Note: O(n²) copying. Use linkedLists to concat them in one go at the end.
92+
# https://github.com/NixOS/nixpkgs/pull/452088
93+
newList = [ elem ] ++ list;
94+
newSet = set // {
95+
${k} = null;
96+
};
97+
in
98+
builtins.seq newList builtins.seq newSet {
99+
list = newList;
100+
set = newSet;
101+
}
102+
)
103+
{
104+
list = [ ];
105+
set = { };
106+
}
107+
l;
108+
in
109+
r.list;
110+
111+
in
112+
6113
{ pkgs }:
7114

115+
# TODO: don't use nix-util for this?
8116
pkgs.nixComponents2.nix-util.overrideAttrs (
9-
attrs:
117+
finalAttrs: prevAttrs:
10118

11119
let
12120
stdenv = pkgs.nixDependencies2.stdenv;
@@ -21,13 +129,89 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
21129
"-D${prefix}:${rest}";
22130
havePerl = stdenv.buildPlatform == stdenv.hostPlatform && stdenv.hostPlatform.isUnix;
23131
ignoreCrossFile = flags: builtins.filter (flag: !(lib.strings.hasInfix "cross-file" flag)) flags;
132+
133+
activeComponents = buildInputsClosureCond isInternal (
134+
lib.attrValues (finalAttrs.passthru.config.getComponents allComponents)
135+
);
136+
137+
allComponents = lib.filterAttrs (k: v: lib.isDerivation v) pkgs.nixComponents2;
138+
internalDrvs = byDrvPath (
139+
# Drop the attr names (not present in buildInputs anyway)
140+
lib.attrValues allComponents
141+
++ lib.concatMap (c: lib.attrValues c.tests or { }) (lib.attrValues allComponents)
142+
);
143+
144+
isInternal =
145+
dep: internalDrvs ? ${builtins.unsafeDiscardStringContext dep.drvPath or "_non-existent_"};
146+
24147
in
25148
{
26-
pname = "shell-for-" + attrs.pname;
149+
pname = "shell-for-nix";
150+
151+
passthru = {
152+
inherit activeComponents;
153+
154+
# We use this attribute to store non-derivation values like functions and
155+
# perhaps other things that are primarily for overriding and not the shell.
156+
config = {
157+
# Default getComponents
158+
getComponents =
159+
c:
160+
builtins.removeAttrs c (
161+
lib.optionals (!havePerl) [ "nix-perl-bindings" ]
162+
++ lib.optionals (!buildCanExecuteHost) [ "nix-manual" ]
163+
);
164+
};
165+
166+
/**
167+
Produce a devShell for a given set of nix components
168+
169+
Example:
170+
171+
```nix
172+
shell.withActiveComponents (c: {
173+
inherit (c) nix-util;
174+
})
175+
```
176+
*/
177+
withActiveComponents =
178+
f2:
179+
finalAttrs.finalPackage.overrideAttrs (
180+
finalAttrs: prevAttrs: {
181+
passthru = prevAttrs.passthru // {
182+
config = prevAttrs.passthru.config // {
183+
getComponents = f2;
184+
};
185+
};
186+
}
187+
);
188+
189+
small =
190+
(finalAttrs.finalPackage.withActiveComponents (c: {
191+
inherit (c)
192+
nix-cli
193+
nix-util-tests
194+
nix-store-tests
195+
nix-expr-tests
196+
nix-fetchers-tests
197+
nix-flake-tests
198+
nix-functional-tests
199+
# Currently required
200+
nix-perl-bindings
201+
;
202+
})).overrideAttrs
203+
(o: {
204+
mesonFlags = o.mesonFlags ++ [
205+
# TODO: infer from activeComponents or vice versa
206+
"-Dkaitai-struct-checks=false"
207+
"-Djson-schema-checks=false"
208+
];
209+
});
210+
};
27211

28212
# Remove the version suffix to avoid unnecessary attempts to substitute in nix develop
29213
version = lib.fileContents ../.version;
30-
name = attrs.pname;
214+
name = finalAttrs.pname;
31215

32216
installFlags = "sysconfdir=$(out)/etc";
33217
shellHook = ''
@@ -98,17 +282,9 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
98282
nativeBuildInputs =
99283
let
100284
inputs =
101-
attrs.nativeBuildInputs or [ ]
102-
++ pkgs.nixComponents2.nix-util.nativeBuildInputs
103-
++ pkgs.nixComponents2.nix-store.nativeBuildInputs
104-
++ pkgs.nixComponents2.nix-fetchers.nativeBuildInputs
105-
++ pkgs.nixComponents2.nix-expr.nativeBuildInputs
106-
++ lib.optionals havePerl pkgs.nixComponents2.nix-perl-bindings.nativeBuildInputs
107-
++ lib.optionals buildCanExecuteHost pkgs.nixComponents2.nix-manual.externalNativeBuildInputs
108-
++ pkgs.nixComponents2.nix-internal-api-docs.nativeBuildInputs
109-
++ pkgs.nixComponents2.nix-external-api-docs.nativeBuildInputs
110-
++ pkgs.nixComponents2.nix-functional-tests.externalNativeBuildInputs
111-
++ pkgs.nixComponents2.nix-json-schema-checks.externalNativeBuildInputs
285+
dedupByString (v: "${v}") (
286+
lib.filter (x: !isInternal x) (lib.lists.concatMap (c: c.nativeBuildInputs) activeComponents)
287+
)
112288
++ lib.optional (
113289
!buildCanExecuteHost
114290
# Hack around https://github.com/nixos/nixpkgs/commit/bf7ad8cfbfa102a90463433e2c5027573b462479
@@ -117,9 +293,7 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
117293
&& lib.meta.availableOn stdenv.buildPlatform (stdenv.hostPlatform.emulator pkgs.buildPackages)
118294
) pkgs.buildPackages.mesonEmulatorHook
119295
++ [
120-
pkgs.buildPackages.cmake
121296
pkgs.buildPackages.gnused
122-
pkgs.buildPackages.changelog-d
123297
modular.pre-commit.settings.package
124298
(pkgs.writeScriptBin "pre-commit-hooks-install" modular.pre-commit.settings.installationScript)
125299
pkgs.buildPackages.nixfmt-rfc-style
@@ -136,18 +310,22 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
136310
# from making its way into NIX_CFLAGS_COMPILE.
137311
lib.filter (p: !lib.hasInfix "separate-debug-info" p) inputs;
138312

313+
propagatedNativeBuildInputs = dedupByString (v: "${v}") (
314+
lib.filter (x: !isInternal x) (
315+
lib.lists.concatMap (c: c.propagatedNativeBuildInputs) activeComponents
316+
)
317+
);
318+
139319
buildInputs = [
140320
pkgs.gbenchmark
141321
]
142-
++ attrs.buildInputs or [ ]
143-
++ pkgs.nixComponents2.nix-util.buildInputs
144-
++ pkgs.nixComponents2.nix-store.buildInputs
145-
++ pkgs.nixComponents2.nix-store-tests.externalBuildInputs
146-
++ pkgs.nixComponents2.nix-fetchers.buildInputs
147-
++ pkgs.nixComponents2.nix-expr.buildInputs
148-
++ pkgs.nixComponents2.nix-expr.externalPropagatedBuildInputs
149-
++ pkgs.nixComponents2.nix-cmd.buildInputs
150-
++ lib.optionals havePerl pkgs.nixComponents2.nix-perl-bindings.externalBuildInputs
322+
++ dedupByString (v: "${v}") (
323+
lib.filter (x: !isInternal x) (lib.lists.concatMap (c: c.buildInputs) activeComponents)
324+
)
151325
++ lib.optional havePerl pkgs.perl;
326+
327+
propagatedBuildInputs = dedupByString (v: "${v}") (
328+
lib.filter (x: !isInternal x) (lib.lists.concatMap (c: c.propagatedBuildInputs) activeComponents)
329+
);
152330
}
153331
)

0 commit comments

Comments
 (0)