diff options
Diffstat (limited to 'infra/libkookie/nixpkgs/pkgs/development/haskell-modules/make-package-set.nix')
-rw-r--r-- | infra/libkookie/nixpkgs/pkgs/development/haskell-modules/make-package-set.nix | 209 |
1 files changed, 174 insertions, 35 deletions
diff --git a/infra/libkookie/nixpkgs/pkgs/development/haskell-modules/make-package-set.nix b/infra/libkookie/nixpkgs/pkgs/development/haskell-modules/make-package-set.nix index 1418cfef0574..4ae3f0b2427f 100644 --- a/infra/libkookie/nixpkgs/pkgs/development/haskell-modules/make-package-set.nix +++ b/infra/libkookie/nixpkgs/pkgs/development/haskell-modules/make-package-set.nix @@ -221,30 +221,56 @@ in package-set { inherit pkgs stdenv callPackage; } self // { # , overrides : Defaulted (HaskellPackageOverrideSet) # , modifier : Defaulted # , returnShellEnv : Defaulted + # , withHoogle : Defaulted + # , cabal2nixOptions : Defaulted # } -> NixShellAwareDerivation + # # Given a path to a haskell package directory, an optional package name # which defaults to the base name of the path, an optional set of source # overrides as appropriate for the 'packageSourceOverrides' function, an # optional set of arbitrary overrides, and an optional haskell package # modifier, return a derivation appropriate for nix-build or nix-shell to # build that package. + # + # If 'returnShellEnv' is true this returns a derivation which will give you + # an environment suitable for developing the listed packages with an + # incremental tool like cabal-install. + # + # If 'withHoogle' is true (the default if a shell environment is requested) + # then 'ghcWithHoogle' is used to generate the derivation (instead of + # 'ghcWithPackages'), see the documentation there for more information. + # + # 'cabal2nixOptions' can contain extra command line arguments to pass to + # 'cabal2nix' when generating the package derivation, for example setting + # a cabal flag with '--flag=myflag'. developPackage = { root , name ? builtins.baseNameOf root , source-overrides ? {} , overrides ? self: super: {} , modifier ? drv: drv - , returnShellEnv ? pkgs.lib.inNixShell }: + , returnShellEnv ? pkgs.lib.inNixShell + , withHoogle ? returnShellEnv + , cabal2nixOptions ? "" }: let drv = (extensible-self.extend (pkgs.lib.composeExtensions (self.packageSourceOverrides source-overrides) overrides)) - .callCabal2nix name root {}; - in if returnShellEnv then (modifier drv).env else modifier drv; + .callCabal2nixWithOptions name root cabal2nixOptions {}; + in if returnShellEnv + then (modifier drv).envFunc {inherit withHoogle;} + else modifier drv; ghcWithPackages = selectFrom: withPackages (selectFrom self); + # Put 'hoogle' into the derivation's PATH with a database containing all + # the package's dependencies; run 'hoogle server --local' in a shell to + # host a search engine for the dependencies. + # + # To reload the Hoogle server automatically on .cabal file changes try + # this: + # echo *.cabal | entr -r -- nix-shell --run 'hoogle server --local' ghcWithHoogle = selectFrom: let packages = selectFrom self; @@ -258,6 +284,7 @@ in package-set { inherit pkgs stdenv callPackage; } self // { # packages themselves. Using nix-shell on this derivation will # give you an environment suitable for developing the listed # packages with an incremental tool like cabal-install. + # # In addition to the "packages" arg and "withHoogle" arg, anything that # can be passed into stdenv.mkDerivation can be included in the input attrset # @@ -274,7 +301,7 @@ in package-set { inherit pkgs stdenv callPackage; } self // { # (import ./.).shellFor { # packages = p: [p.frontend p.backend p.common]; # withHoogle = true; - # buildInputs = [ pkgs.python ]; + # buildInputs = [ pkgs.python pkgs.cabal-install ]; # } # # -- cabal.project @@ -285,38 +312,150 @@ in package-set { inherit pkgs stdenv callPackage; } self // { # # bash$ nix-shell --run "cabal new-build all" # bash$ nix-shell --run "python" - shellFor = { packages, withHoogle ? false, ... } @ args: + shellFor = + { # Packages to create this development shell for. These are usually + # your local packages. + packages + , # Whether or not to generate a Hoogle database for all the + # dependencies. + withHoogle ? false + , # Whether or not to include benchmark dependencies of your local + # packages. You should set this to true if you have benchmarks defined + # in your local packages that you want to be able to run with cabal benchmark + doBenchmark ? false + , ... + } @ args: let - combinedPackageFor = packages: - let - selected = packages self; - - pname = if pkgs.lib.length selected == 1 - then (pkgs.lib.head selected).name - else "packages"; - - # If `packages = [ a b ]` and `a` depends on `b`, don't build `b`, - # because cabal will end up ignoring that built version, assuming - # new-style commands. - combinedPackages = pkgs.lib.filter - (input: pkgs.lib.all (p: input.outPath or null != p.outPath) selected); - - # Returns an attrset containing a combined list packages' inputs for each - # stage of the build process - packageInputs = pkgs.lib.zipAttrsWith - (_: pkgs.lib.concatMap combinedPackages) - (map (p: p.getCabalDeps) selected); - - genericBuilderArgs = { - inherit pname; - version = "0"; - license = null; - } // packageInputs; - - in self.mkDerivation genericBuilderArgs; - - mkDerivationArgs = builtins.removeAttrs args [ "packages" "withHoogle" ]; - in ((combinedPackageFor packages).envFunc { inherit withHoogle; }).overrideAttrs (old: mkDerivationArgs // { + # A list of the packages we want to build a development shell for. + # This is a list of Haskell package derivations. + selected = packages self; + + # This is a list of attribute sets, where each attribute set + # corresponds to the build inputs of one of the packages input to shellFor. + # + # Each attribute has keys like buildDepends, executableHaskellDepends, + # testPkgconfigDepends, etc. The values for the keys of the attribute + # set are lists of dependencies. + # + # Example: + # cabalDepsForSelected + # => [ + # # This may be the attribute set corresponding to the `backend` + # # package in the example above. + # { buildDepends = [ gcc ... ]; + # libraryHaskellDepends = [ lens conduit ... ]; + # ... + # } + # # This may be the attribute set corresponding to the `common` + # # package in the example above. + # { testHaskellDepends = [ tasty hspec ... ]; + # libraryHaskellDepends = [ lens aeson ]; + # benchmarkHaskellDepends = [ criterion ... ]; + # ... + # } + # ... + # ] + cabalDepsForSelected = map (p: p.getCabalDeps) selected; + + # A predicate that takes a derivation as input, and tests whether it is + # the same as any of the `selected` packages. + # + # Returns true if the input derivation is not in the list of `selected` + # packages. + # + # isNotSelected :: Derivation -> Bool + # + # Example: + # + # isNotSelected common [ frontend backend common ] + # => false + # + # isNotSelected lens [ frontend backend common ] + # => true + isNotSelected = input: pkgs.lib.all (p: input.outPath or null != p.outPath) selected; + + # A function that takes a list of list of derivations, filters out all + # the `selected` packages from each list, and concats the results. + # + # zipperCombinedPkgs :: [[Derivation]] -> [Derivation] + # + # Example: + # zipperCombinedPkgs [ [ lens conduit ] [ aeson frontend ] ] + # => [ lens conduit aeson ] + # + # Note: The reason this isn't just the function `pkgs.lib.concat` is + # that we need to be careful to remove dependencies that are in the + # `selected` packages. + # + # For instance, in the above example, if `common` is a dependency of + # `backend`, then zipperCombinedPkgs needs to be careful to filter out + # `common`, because cabal will end up ignoring that built version, + # assuming new-style commands. + zipperCombinedPkgs = vals: + pkgs.lib.concatMap + (drvList: pkgs.lib.filter isNotSelected drvList) + vals; + + # Zip `cabalDepsForSelected` into a single attribute list, combining + # the derivations in all the individual attributes. + # + # Example: + # packageInputs + # => # Assuming the value of cabalDepsForSelected is the same as + # # the example in cabalDepsForSelected: + # { buildDepends = [ gcc ... ]; + # libraryHaskellDepends = [ lens conduit aeson ... ]; + # testHaskellDepends = [ tasty hspec ... ]; + # benchmarkHaskellDepends = [ criterion ... ]; + # ... + # } + # + # See the Note in `zipperCombinedPkgs` for what gets filtered out from + # each of these dependency lists. + packageInputs = + pkgs.lib.zipAttrsWith (_name: zipperCombinedPkgs) cabalDepsForSelected; + + # A attribute set to pass to `haskellPackages.mkDerivation`. + # + # The important thing to note here is that all the fields from + # packageInputs are set correctly. + genericBuilderArgs = { + pname = + if pkgs.lib.length selected == 1 + then (pkgs.lib.head selected).name + else "packages"; + version = "0"; + license = null; + } + // packageInputs + // pkgs.lib.optionalAttrs doBenchmark { + # `doBenchmark` needs to explicitly be set here because haskellPackages.mkDerivation defaults it to `false`. If the user wants benchmark dependencies included in their development shell, it has to be explicitly enabled here. + doBenchmark = true; + }; + + # This is a pseudo Haskell package derivation that contains all the + # dependencies for the packages in `selected`. + # + # This is a derivation created with `haskellPackages.mkDerivation`. + # + # pkgWithCombinedDeps :: HaskellDerivation + pkgWithCombinedDeps = self.mkDerivation genericBuilderArgs; + + # The derivation returned from `envFunc` for `pkgWithCombinedDeps`. + # + # This is a derivation that can be run with `nix-shell`. It provides a + # GHC with a package database with all the dependencies of our + # `selected` packages. + # + # This is a derivation created with `stdenv.mkDerivation` (not + # `haskellPackages.mkDerivation`). + # + # pkgWithCombinedDepsDevDrv :: Derivation + pkgWithCombinedDepsDevDrv = pkgWithCombinedDeps.envFunc { inherit withHoogle; }; + + mkDerivationArgs = builtins.removeAttrs args [ "packages" "withHoogle" "doBenchmark" ]; + + in pkgWithCombinedDepsDevDrv.overrideAttrs (old: mkDerivationArgs // { nativeBuildInputs = old.nativeBuildInputs ++ mkDerivationArgs.nativeBuildInputs or []; buildInputs = old.buildInputs ++ mkDerivationArgs.buildInputs or []; }); |