on iko/ghcjs-patch-master help sign in
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 https://github.com/input-output-hk/haskell.nix/issues/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.

commit2db0b608168c4b6bacd7ea3d762f664e0a150eff authorMichael Peyton Jones <michael.peyton-jones@iohk.io> dateFri 14 Aug 2020, 03:06 (6 years ago) parent38cce7c
1 file changed+46−27
builder/shell-for.nix+46−27
builder/shell-for.nix+46−27
@@ -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 @@
2425let
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.