on iko/ghcjs-patch-master help sign in
Allows project to be easily extended. (#1467)

* Allows project to be easily extended. * Add common project overlays

commitb66b0445ebec8842270d91d945227898265a114a authorJean-Baptiste Giraudeau <jean-baptiste.giraudeau@iohk.io> dateFri 13 May 2022, 11:21 (4 years ago) parentf1bb3aa
4 files changed+136−6
docs/reference/library.md+1 lib/default.nix+4 lib/project-overlays.nix+114 overlays/haskell.nix+17−6
docs/reference/library.md+1
@@ -167,6 +167,7 @@ Then feeding its result into [mkCabalProjectPkgSet](#mkcabalprojectpkgset) passi
167167| `ghcWithPackages` | Function | [`ghcWithPackages`](#ghcwithpackages) |
168168| `projectCross` | Attrset | Like `pkgs.pkgsCross.<system>` from nixpkgs `p.projectCross.<system>` returns the project results for cross compilation (where system is a member of nixpkgs lib.systems.examples). So `p.projectCross.ghcjs.hsPkgs` is the same as `hsPkgs` but compiled with ghcjs |
169169| `appendModule` | Function | Re-eval the project with an extra module (or module list). |
170+| `extend` and `appendOverlays` | Function | Modify a project, or add attributes, through overlays: `p.extend(final: prev: { })`. The overlays are carried-over `projectCross` and `appendModule` invocations. |
170171
171172## project, cabalProject and stackProject
172173
lib/default.nix+4
@@ -340,4 +340,8 @@ in {
340340 # Converts from a `compoent.depends` value to a library derivation.
341341 # In the case of sublibs the `depends` value should already be the derivation.
342342 dependToLib = d: d.components.library or d;
343+
344+ projectOverlays = import ./project-overlays.nix {
345+ inherit lib haskellLib;
346+ };
343347}
lib/project-overlays.nixnew file+114
@@ -0,0 +1,114 @@
1+{
2+ lib
3+, haskellLib
4+}: {
5+ devshell = final: prev: {
6+ devshell = let
7+ cabal-install = final.pkgs.buildPackages.haskell-nix.cabal-install.${final.args.compiler-nix-name};
8+ inherit (final.pkgs.buildPackages) runCommand runtimeShell;
9+ cabalWrapper = runCommand "cabal" { inherit (cabal-install) meta; } ''
10+ mkdir -p $out/bin
11+ cat << EOF > $out/bin/cabal
12+ #!${runtimeShell}
13+ set -euo pipefail
14+
15+ find_up() {
16+ while [[ \$PWD != / ]] ; do
17+ if [[ -e "\$1" ]]; then
18+ echo "\$PWD"
19+ return
20+ fi
21+ cd ..
22+ done
23+ }
24+
25+ toplevel=\$(find_up "cabal.project")
26+
27+ if [[ -n "\$toplevel" ]]; then
28+ cabal_project="\$toplevel/cabal.project"
29+ nix_cabal_project=\$toplevel/.nix-cabal.project
30+ extra_cabal_opts=("--project-file=\$nix_cabal_project")
31+ awk '
32+ # Add comment with explanation of file
33+ BEGIN {
34+ print "-- Generated from '"\$cabal_project"' by the wrapper script"
35+ print "-- ${placeholder "out"}/cabal"
36+ print "-- Add this file to your .gitignore\n"
37+ }
38+
39+ # Matches all section starts
40+ /^[^ ]/ {
41+ # Remember the section name (they can span multiple lines)
42+ section = \$0
43+ }
44+ # Matches every line
45+ // {
46+ # Only print the line if it is not in the section we want to omit
47+ if (section != "source-repository-package")
48+ print \$0
49+ }
50+ ' "\$cabal_project" > "\$nix_cabal_project"
51+ else
52+ extra_cabal_opts=()
53+ fi
54+
55+ cabal=${placeholder "out"}/bin/.cabal
56+ >&2 echo "\$cabal \''${extra_cabal_opts[@]} \$@"
57+ exec "\$cabal" "\''${extra_cabal_opts[@]}" "\$@"
58+ EOF
59+ cp -rn ${cabal-install}/* $out/
60+ cp ${cabal-install}/bin/cabal $out/bin/.cabal
61+ chmod +x $out/bin/*
62+ '';
63+ in {
64+ packages = final.shell.nativeBuildInputs;
65+ env = lib.mapAttrsToList lib.nameValuePair {
66+ inherit (final.shell) CABAL_CONFIG NIX_GHC_LIBDIR;
67+ };
68+ commands = [
69+ {
70+ package = cabalWrapper;
71+ category = "development";
72+ }
73+ ];
74+ };
75+ };
76+
77+ projectComponents = final: prev: let
78+ packages = haskellLib.selectProjectPackages final.hsPkgs;
79+ # used to materialize `packages-exes.nix` (project packages and exes list) to avoid some evaluations:
80+ packagesExes = lib.genAttrs
81+ (lib.attrNames final.packages)
82+ (name: lib.attrNames final.hsPkgs.${name}.components.exes);
83+ collectExes = packagesExes: lib.listToAttrs (lib.concatLists (lib.mapAttrsToList
84+ (p: map (exe: lib.nameValuePair exe final.hsPkgs.${p}.components.exes.${exe})) packagesExes));
85+ in {
86+ # local project packages:
87+ packages = haskellLib.selectProjectPackages final.hsPkgs;
88+ # set of all exes (as first level entries):
89+ exes = collectExes packagesExes;
90+ # set of all exes (as first level entries), mapped from a materialized set of packages and exes set (generated via `generatePackagesExes`):
91+ exesFrom = packagesExesMat: collectExes (import packagesExesMat);
92+ # `tests` are the test suites which have been built.
93+ tests = haskellLib.collectComponents' "tests" final.packages;
94+ # `benchmarks` (only built, not run).
95+ benchmarks = haskellLib.collectComponents' "benchmarks" final.packages;
96+ # `checks` collect results of executing the tests:
97+ checks = haskellLib.collectChecks' final.packages;
98+
99+ generatePackagesExesMat = let
100+ nixFile = builtins.toFile "packages-exes.nix" (lib.generators.toPretty {} packagesExes);
101+ in final.pkgs.buildPackages.writeShellScript "generate-packages-exes" ''
102+ set -euo pipefail
103+
104+ TARGET="$1"
105+ TARGET_DIR="$(dirname "$TARGET")"
106+
107+ mkdir -p "$TARGET_DIR"
108+ rm -rf "$TARGET"
109+ cp -r ${nixFile} "$TARGET"
110+ chmod -R +w "$TARGET"
111+ '';
112+ };
113+
114+}
overlays/haskell.nix+17−6
@@ -546,12 +546,19 @@ final: prev: {
546546 };
547547 in project);
548548
549−
550549 # Take `hsPkgs` from the `rawProject` and update all the packages and
551550 # components so they have a `.project` attribute and as well as
552551 # a `.package` attribute on the components.
553− addProjectAndPackageAttrs = rawProject:
554− final.lib.fix (project':
552+ addProjectAndPackageAttrs = let
553+ # helper function similar to nixpkgs 'makeExtensible' but that keep track
554+ # of extension function so that it can be reused to extend another project:
555+ makeExtensible = f: rattrs: final.lib.fix (final.lib.extends f (self: rattrs self // {
556+ __overlay__ = f;
557+ extend = f: makeExtensible (final.lib.composeExtensions self.__overlay__ f) rattrs;
558+ appendOverlays = extraOverlays: self.extend (final.lib.composeManyExtensions ([self.__overlay__] ++ extraOverlays));
559+ }));
560+ in rawProject:
561+ makeExtensible (final: prev: {}) (project':
555562 let project = project' // { recurseForDerivations = false; };
556563 in rawProject // rec {
557564 # It is often handy to be able to get nix pkgs from the project.
@@ -601,17 +608,21 @@ final: prev: {
601608 # `projectCross.<system>` where system is a member of nixpkgs lib.systems.examples.
602609 # See https://nixos.wiki/wiki/Cross_Compiling
603610 projectCross = (final.lib.mapAttrs (_: pkgs:
604− rawProject.projectFunction pkgs.haskell-nix rawProject.projectModule
611+ (rawProject.projectFunction pkgs.haskell-nix rawProject.projectModule)
612+ # Re-apply overlay from original project:
613+ .extend project.__overlay__
605614 ) final.pkgsCross) // { recurseForDerivations = false; };
606615
607616 # re-eval this project with an extra module (or module list).
608− appendModule = extraProjectModule: rawProject.projectFunction final.haskell-nix
617+ appendModule = extraProjectModule: (rawProject.projectFunction final.haskell-nix
609618 ((if builtins.isList rawProject.projectModule
610619 then rawProject.projectModule
611620 else [rawProject.projectModule])
612621 ++ (if builtins.isList extraProjectModule
613622 then extraProjectModule
614− else [extraProjectModule]));
623+ else [extraProjectModule])))
624+ # Re-apply overlay from original project:
625+ .extend project.__overlay__;
615626
616627 # Add support for passing in `crossPlatforms` argument.
617628 # crossPlatforms is an easy way to include the inputs for a basic