Skip to content

Fix: shellFor doesn't filter based on transitive dependencies #1145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 21, 2021

Conversation

TeofilC
Copy link
Contributor

@TeofilC TeofilC commented Jun 20, 2021

Closes #1143

This works by taking the components specified for a shellFor as a seed and grow it to be the minimum set such that:

  • it contains the seed
  • no component is both a transitive dependency of something in the set and depends transitively on something in the set
    This larger set is then used with the existing logic to include dependencies and filter out our selected components.

I've tested this on a trivial reproducer, but haven't done much more testing yet.
Does this work with the source-repository-package case you encountered @michaelpj ?

Hopefully the comments are still understandable. Let me know if I can change anything to make it clearer.

This should solve the specific performance problems pointed out in #952 .
But it requires the transitive closure of the selected components dependencies to be traversed once. So, really this might be a performance regression.
A way to mitigate this might be to only look at transitive dependencies that are local or project packages, but I'm not sure if that would break correctness, and would require a new version of the flatLibDepends.

@TeofilC TeofilC force-pushed the shellForTransitiveDeps branch from 1293306 to 0c0d3f4 Compare June 20, 2021 16:40
ensure that the following invariant is enforced:
  `shellFor` never adds the selected packages to its environment
@TeofilC TeofilC force-pushed the shellForTransitiveDeps branch from 0c0d3f4 to 85a5b4a Compare June 20, 2021 17:01
@hamishmack
Copy link
Collaborator

This seems to work great! I have had exactly this issue with leksah recently. There is a patched version of gi-gobject in the repo and without this fix all the dependencies of gi-gobject are compiled twice (once to make the nix-shell and then again in the shell with cabal build). This was particularly painful as some of them (like gi-gtk) take a while to build.

I added or false to fix this error:

error: attribute 'pkg-config-wrapper-0.29.2' missing

       at /nix/store/jg6lcpcy7j5ph28cd2p374r8s170f46d-haskellNix-src/builder/shell-for.nix:70:26:

           69|   removeSelectedInputs =
           70|     lib.filter (input: !(selectedComponentsBitmap."${((haskellLib.dependToLib input).name or null)}"));
             |                          ^
           71|

I also did a quick sanity check on the performance and could see no noticeable difference in the shell startup time.

@hamishmack
Copy link
Collaborator

bors try

iohk-bors bot added a commit that referenced this pull request Jun 21, 2021
@iohk-bors
Copy link
Contributor

iohk-bors bot commented Jun 21, 2021

@hamishmack hamishmack merged commit d7e5cfa into input-output-hk:master Jun 21, 2021
@michaelpj
Copy link
Collaborator

Unfortunately, this seems to ~double the evaluation time for our shell.nix, which is quite bad.

@TeofilC
Copy link
Contributor Author

TeofilC commented Jun 23, 2021

I can take a look at optimising this. Doubling evaluation time does sound quite bad.

I have two ideas:

  1. right now we look at all the transitive dependencies, but we could only look at local packages. That's assuming that a user doesn't want to select a global package and that global packages can't depend on locals, but these seem like reasonable assumptions to me.
  2. right now we traverse the transitive dependencies twice, but we should get away with doing it just once

@michaelpj
Copy link
Collaborator

I haven't looked at it in detail, but I suspect we're doing something quadratic. Given it's a graph algorithm and we're implementing it with lists, we might not be able to avoid that!

But reducing the size would be good. Especially if it's (total number of packages)^2 vs (number of local packages)^2.

@TeofilC
Copy link
Contributor Author

TeofilC commented Jun 23, 2021

Yeah, we are definitely doing something O(number of edges), which is worst case quadratic if the graph is really dense.
I don't think we're suffering too much from list based quadratic behaviour, since flatLibDepends is implemented in terms of builtings.genericClosure, which uses sets under the hood (https://github.com/NixOS/nix/blob/323e5450a1a6e4eb97ba1c9aeba195187cfaff37/src/libexpr/primops.cc#L625).

I've opened #1146 , which implements the local packages optimisation. Hopefully that mitigates the performance regression

booniepepper pushed a commit to booniepepper/haskell.nix that referenced this pull request Feb 4, 2022
…output-hk#1145)

* Fix shellFor not accounting for transitive dependencies

ensure that the following invariant is enforced:
  `shellFor` never adds the selected packages to its environment

* Fix missing selectedComponentsBitmap attribute

Co-authored-by: Hamish Mackenzie <Hamish.K.Mackenzie@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

shellFor doesn't filter based on transitive dependencies
3 participants