Skip to content

Commit 2db0b60

Browse files
authored
Fix dependency filtering for shellFor (#819)
I had to refactor this a bit to understand what was going on, but the kicker is two changes: - `removeSelectedInput` was only removing *library* components. This was fine when we were only looking at the `all` components, since they always dependend on libraries. But now we're looking at all kinds of components, and these can, for example, depend on internal libraries. So we need to filter out dependencies on *any* component, not just a library component. This fixes #818 - The filtering on `nativeBuildInputs` was wrong, since it was using the filtering function that filters *packages*, not the one that filters *derivations*. I didn't observe this causing a bug but it seems wrong.
1 parent 38cce7c commit 2db0b60

File tree

1 file changed

+46
-27
lines changed

1 file changed

+46
-27
lines changed

builder/shell-for.nix

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# worked on in the shell then we can pass a different `components` function
1616
# to select those.
1717
, components ? ps: lib.concatMap haskellLib.getAllComponents (packages hsPkgs)
18+
# Additional packages to be added unconditionally
1819
, additional ? _: []
1920
, withHoogle ? true
2021
, exactDeps ? false
@@ -24,39 +25,57 @@
2425
let
2526
# TODO find out why hoogle index creation can be made to work for cross compilers
2627
withHoogle' = withHoogle && !haskellLib.isCrossHost;
27-
selected = packages hsPkgs;
28+
29+
selectedPackages = packages hsPkgs;
30+
additionalPackages = additional hsPkgs;
2831
selectedComponents = components hsPkgs;
29-
additionalSelected = additional hsPkgs;
30-
selectedConfigs = map (c: c.config) selectedComponents;
3132

32-
name = if lib.length selected == 1
33-
then "ghc-shell-for-${(lib.head selected).identifier.name}"
33+
# The configs of all the selected components
34+
selectedConfigs = map (c: c.config) selectedComponents ++ map (p: p.setup.config) selectedPackages;
35+
36+
name = if lib.length selectedPackages == 1
37+
then "ghc-shell-for-${(lib.head selectedPackages).identifier.name}"
3438
else "ghc-shell-for-packages";
3539

36-
# Removes the selected packages from a list of packages.
37-
# If `packages = [ a b ]` and `a` depends on `b`, don't build `b`,
38-
# because cabal will end up ignoring that built version;
39-
removeSelected = lib.filter
40-
(input: lib.all (cfg: (input.identifier or null) != cfg.identifier) selected);
41-
# Also since a will include the library component of b in its buildInputs
42-
# (to make `propagatedBuildInputs` of `pkgconfig-depends` work) those should
43-
# also be excluded (the pkgconfig depends of b will still be included in the
44-
# system shells buildInputs via b's own buildInputs).
45-
removeSelectedInputs = lib.filter
46-
(input: lib.all (cfg: input != cfg.components.library or null) selected);
47-
48-
packageInputs =
49-
removeSelected
50-
(lib.concatMap (cfg: cfg.depends) selectedConfigs
51-
++ lib.concatMap (cfg: cfg.setup.config.depends or []) selected
52-
)
53-
++ additionalSelected;
54-
55-
# Add the system libraries and build tools of the selected haskell
56-
# packages to the shell.
40+
# Given a `packages = [ a b ]` we construct a shell with the dependencies of `a` and `b`.
41+
#
42+
# But if `a` depends on `b`, we don't want to include `b`, because
43+
# - cabal will end up ignoring that built version;
44+
# - The user has indicated that's what they're working on, so they probably don't want to have to build
45+
# it first (and it will change often).
46+
# Generally we never want to include any of (the components of) the selected packages as dependencies.
47+
#
48+
# Furthermore, if `a` depends on `b`, `a` will include the library component of `b` in its `buildInputs`
49+
# (to make `propagatedBuildInputs` of `pkgconfig-depends` work). So we also need to filter those
50+
# (the pkgconfig depends of `b` will still be included in the
51+
# system shell's `buildInputs` via `b`'s own `buildInputs`).
52+
# We have to do this slightly differently because we will be looking at the actual components rather
53+
# than the packages.
54+
55+
# Given a list of packages, removes those which were selected as part of the shell.
56+
# We do this on the basis of their identifiers being the same, not direct equality (why?).
57+
removeSelectedPackages =
58+
# All the identifiers of the selected packages
59+
let selectedPackageIds = map (p: p.identifier) selectedPackages;
60+
in lib.filter (input: !(builtins.elem (input.identifier or null) selectedPackageIds));
61+
62+
# Given a list of derivations, removes those which are components of packages which were selected as part of the shell.
63+
removeSelectedInputs =
64+
# All the components of the selected packages: we shouldn't add any of these as dependencies
65+
let selectedPackageComponents = lib.concatMap haskellLib.getAllComponents selectedPackages;
66+
in lib.filter (input: !(builtins.elem input selectedPackageComponents));
67+
68+
# We need to remove any dependencies which are selected packages (see above).
69+
# `depends` contains packages so we use 'removeSelectedPackages`.
70+
packageInputs = removeSelectedPackages (lib.concatMap (cfg: cfg.depends) selectedConfigs) ++ additionalPackages;
71+
72+
# Add the system libraries and build tools of the selected haskell packages to the shell.
73+
# We need to remove any inputs which are selected components (see above).
74+
# `buildInputs`, `propagatedBuildInputs`, and `executableToolDepends` contain component
75+
# derivations, not packages, so we use `removeSelectedInputs`).
5776
systemInputs = removeSelectedInputs (lib.concatMap
5877
(c: c.buildInputs ++ c.propagatedBuildInputs) selectedComponents);
59-
nativeBuildInputs = removeSelected
78+
nativeBuildInputs = removeSelectedInputs
6079
(lib.concatMap (c: c.executableToolDepends) selectedComponents);
6180

6281
# Set up a "dummy" component to use with ghcForComponent.

0 commit comments

Comments
 (0)