@@ -730,4 +730,148 @@ rec {
730730 libraryPkgconfigDepends = propagatedPlainBuildInputs old . libraryPkgconfigDepends or [ ] ;
731731 testPkgconfigDepends = propagatedPlainBuildInputs old . testPkgconfigDepends or [ ] ;
732732 } ) ;
733+
734+ /*
735+ Get the combined build inputs for a list of Haskell packages.
736+
737+ This combines all of the build input attributes of the packages
738+ (`buildDepends`, `libraryHaskellDepends`, etc.) while being careful to
739+ filter out packages from the arguments from the result.
740+
741+ This returns an attribute set where each key corresponds to a
742+ `haskellPackages.mkDerivation` argument accepting a list of build inputs.
743+
744+ This logic is used by `haskellPackages.shellFor` to determine the packages
745+ which are needed to build a particular set of packages.
746+
747+ ```
748+ > combineInputs { packages = [haskellPackages.lens haskellPackages.aeson]; }
749+ {
750+ buildTools = [ ];
751+ # ...
752+ libraryHaskellDepends = [
753+ «derivation /nix/store/pha8fwh6lc05nm2vvg1sivhyb5nmnkic-assoc-1.1.1.drv»
754+ «derivation /nix/store/9j5w6q1hmf50r9jydj9n7vcplknw4fvv-base-orphans-0.9.3.drv»
755+ # ...
756+ «derivation /nix/store/r28dlymqid6q31dh5a46k36bag60ca52-witherable-0.5.drv»
757+ ];
758+ # ...
759+ testHaskellDepends = [
760+ «derivation /nix/store/rl0kfrl05z94in9f2fk8hl44r9pfyh44-HUnit-1.6.2.0.drv»
761+ «derivation /nix/store/0dbqnww1fb3ml9f4s21phkw901yszd48-QuickCheck-2.14.3.drv»
762+ # ...
763+ «derivation /nix/store/0dml0xsw6s1v1d2qqhyk3qjyl4lw2fi4-vector-0.13.2.0.drv»
764+ ];
765+ }
766+ ```
767+ */
768+ combineInputs =
769+ {
770+ # A list of packages to get the transitive inputs for.
771+ #
772+ # Note that while the `packages` parameter to `haskellPackages.shellFor`
773+ # is a function from the Haskell package set to a list of packages, this
774+ # function takes the list of packages directly.
775+ packages ,
776+
777+ # Extra dependencies, in the form of `haskellPackages.mkDerivation` build
778+ # attributes.
779+ #
780+ # An example use case is when you have Haskell scripts that use
781+ # libraries that don't occur in the dependencies of `packages`.
782+ #
783+ # Example:
784+ #
785+ # extraDependencies = p: {
786+ # libraryHaskellDepends = [ p.releaser ];
787+ # };
788+ extraDependencies ? { } ,
789+
790+ ...
791+ } :
792+ let
793+ # This is a list of attribute sets, where each attribute set
794+ # corresponds to the build inputs of one of the packages input to shellFor.
795+ #
796+ # Each attribute has keys like buildDepends, executableHaskellDepends,
797+ # testPkgconfigDepends, etc. The values for the keys of the attribute
798+ # set are lists of dependencies.
799+ #
800+ # Example:
801+ # cabalDepsForSelected
802+ # => [
803+ # # This may be the attribute set corresponding to the `backend`
804+ # # package in the example above.
805+ # { buildDepends = [ gcc ... ];
806+ # libraryHaskellDepends = [ lens conduit ... ];
807+ # ...
808+ # }
809+ # # This may be the attribute set corresponding to the `common`
810+ # # package in the example above.
811+ # { testHaskellDepends = [ tasty hspec ... ];
812+ # libraryHaskellDepends = [ lens aeson ];
813+ # benchmarkHaskellDepends = [ criterion ... ];
814+ # ...
815+ # }
816+ # ...
817+ # ]
818+ cabalDepsForSelected = builtins . map ( p : p . getCabalDeps ) ( builtins . filter ( p : p != null ) packages ) ;
819+
820+ # A predicate that takes a derivation as input, and tests whether it is
821+ # the same as any of the `packages`.
822+ #
823+ # Returns true if the input derivation is not in the list of `packages`.
824+ #
825+ # isNotSelected :: Derivation -> Bool
826+ #
827+ # Example:
828+ #
829+ # isNotSelected common [ frontend backend common ]
830+ # => false
831+ #
832+ # isNotSelected lens [ frontend backend common ]
833+ # => true
834+ isNotSelected = input : pkgs . lib . all ( p : input . outPath or null != p . outPath ) packages ;
835+
836+ # A function that takes a list of list of derivations, filters out all
837+ # the `packages` from each list, and concats the results.
838+ #
839+ # zipperCombinedPkgs :: [[Derivation]] -> [Derivation]
840+ #
841+ # Example:
842+ # zipperCombinedPkgs [ [ lens conduit ] [ aeson frontend ] ]
843+ # => [ lens conduit aeson ]
844+ #
845+ # Note: The reason this isn't just the function `pkgs.lib.concat` is
846+ # that we need to be careful to remove dependencies that are in the
847+ # `packages` list.
848+ #
849+ # For instance, in the above example, if `common` is a dependency of
850+ # `backend`, then zipperCombinedPkgs needs to be careful to filter out
851+ # `common`, because cabal will end up ignoring that built version,
852+ # assuming new-style commands.
853+ zipperCombinedPkgs = vals : pkgs . lib . concatMap ( drvList : pkgs . lib . filter isNotSelected drvList ) vals ;
854+
855+ # Zip `cabalDepsForSelected` into a single attribute list, combining
856+ # the derivations in all the individual attributes.
857+ #
858+ # Example:
859+ # packageInputs
860+ # => # Assuming the value of cabalDepsForSelected is the same as
861+ # # the example in cabalDepsForSelected:
862+ # { buildDepends = [ gcc ... ];
863+ # libraryHaskellDepends = [ lens conduit aeson ... ];
864+ # testHaskellDepends = [ tasty hspec ... ];
865+ # benchmarkHaskellDepends = [ criterion ... ];
866+ # ...
867+ # }
868+ #
869+ # See the Note in `zipperCombinedPkgs` for what gets filtered out from
870+ # each of these dependency lists.
871+ packageInputs = pkgs . lib . zipAttrsWith ( _name : zipperCombinedPkgs ) (
872+ cabalDepsForSelected ++ [ extraDependencies ]
873+ ) ;
874+ in
875+ packageInputs ;
876+
733877}
0 commit comments