Skip to content

Commit 223c859

Browse files
committed
haskell.lib.combineInputs: init
This adds a new `haskell.lib.combineInputs` function which encapsulates the logic used by `haskellPackages.shellFor` to compute package dependencies.
1 parent 599ee12 commit 223c859

File tree

6 files changed

+392
-82
lines changed

6 files changed

+392
-82
lines changed

pkgs/development/haskell-modules/lib/compose.nix

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

pkgs/development/haskell-modules/lib/default.nix

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,4 +391,6 @@ rec {
391391
# same package in the (recursive) dependencies of the package being
392392
# built. Will delay failures, if any, to compile time.
393393
allowInconsistentDependencies = compose.allowInconsistentDependencies;
394+
395+
combineInputs = compose.combineInputs;
394396
}

pkgs/development/haskell-modules/make-package-set.nix

Lines changed: 4 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -505,88 +505,10 @@ package-set { inherit pkgs lib callPackage; } self
505505
# This is a list of Haskell package derivations.
506506
selected = packages self;
507507

508-
# This is a list of attribute sets, where each attribute set
509-
# corresponds to the build inputs of one of the packages input to shellFor.
510-
#
511-
# Each attribute has keys like buildDepends, executableHaskellDepends,
512-
# testPkgconfigDepends, etc. The values for the keys of the attribute
513-
# set are lists of dependencies.
514-
#
515-
# Example:
516-
# cabalDepsForSelected
517-
# => [
518-
# # This may be the attribute set corresponding to the `backend`
519-
# # package in the example above.
520-
# { buildDepends = [ gcc ... ];
521-
# libraryHaskellDepends = [ lens conduit ... ];
522-
# ...
523-
# }
524-
# # This may be the attribute set corresponding to the `common`
525-
# # package in the example above.
526-
# { testHaskellDepends = [ tasty hspec ... ];
527-
# libraryHaskellDepends = [ lens aeson ];
528-
# benchmarkHaskellDepends = [ criterion ... ];
529-
# ...
530-
# }
531-
# ...
532-
# ]
533-
cabalDepsForSelected = map (p: p.getCabalDeps) selected;
534-
535-
# A predicate that takes a derivation as input, and tests whether it is
536-
# the same as any of the `selected` packages.
537-
#
538-
# Returns true if the input derivation is not in the list of `selected`
539-
# packages.
540-
#
541-
# isNotSelected :: Derivation -> Bool
542-
#
543-
# Example:
544-
#
545-
# isNotSelected common [ frontend backend common ]
546-
# => false
547-
#
548-
# isNotSelected lens [ frontend backend common ]
549-
# => true
550-
isNotSelected = input: pkgs.lib.all (p: input.outPath or null != p.outPath) selected;
551-
552-
# A function that takes a list of list of derivations, filters out all
553-
# the `selected` packages from each list, and concats the results.
554-
#
555-
# zipperCombinedPkgs :: [[Derivation]] -> [Derivation]
556-
#
557-
# Example:
558-
# zipperCombinedPkgs [ [ lens conduit ] [ aeson frontend ] ]
559-
# => [ lens conduit aeson ]
560-
#
561-
# Note: The reason this isn't just the function `pkgs.lib.concat` is
562-
# that we need to be careful to remove dependencies that are in the
563-
# `selected` packages.
564-
#
565-
# For instance, in the above example, if `common` is a dependency of
566-
# `backend`, then zipperCombinedPkgs needs to be careful to filter out
567-
# `common`, because cabal will end up ignoring that built version,
568-
# assuming new-style commands.
569-
zipperCombinedPkgs = vals: pkgs.lib.concatMap (drvList: pkgs.lib.filter isNotSelected drvList) vals;
570-
571-
# Zip `cabalDepsForSelected` into a single attribute list, combining
572-
# the derivations in all the individual attributes.
573-
#
574-
# Example:
575-
# packageInputs
576-
# => # Assuming the value of cabalDepsForSelected is the same as
577-
# # the example in cabalDepsForSelected:
578-
# { buildDepends = [ gcc ... ];
579-
# libraryHaskellDepends = [ lens conduit aeson ... ];
580-
# testHaskellDepends = [ tasty hspec ... ];
581-
# benchmarkHaskellDepends = [ criterion ... ];
582-
# ...
583-
# }
584-
#
585-
# See the Note in `zipperCombinedPkgs` for what gets filtered out from
586-
# each of these dependency lists.
587-
packageInputs = pkgs.lib.zipAttrsWith (_name: zipperCombinedPkgs) (
588-
cabalDepsForSelected ++ [ (extraDependencies self) ]
589-
);
508+
packageInputs = haskellLib.combineInputs {
509+
packages = selected;
510+
extraDependencies = extraDependencies self;
511+
};
590512

591513
# A attribute set to pass to `haskellPackages.mkDerivation`.
592514
#

pkgs/test/haskell/default.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ lib.recurseIntoAttrs {
55
documentationTarball = callPackage ./documentationTarball { };
66
ghcWithPackages = callPackage ./ghcWithPackages { };
77
incremental = callPackage ./incremental { };
8+
lib = callPackage ./lib { };
89
setBuildTarget = callPackage ./setBuildTarget { };
910
shellFor = callPackage ./shellFor { };
1011
upstreamStackHpackVersion = callPackage ./upstreamStackHpackVersion { };

0 commit comments

Comments
 (0)