on iko/ghcjs-patch-master help sign in
Add more flakes support and getting started guide (#972)

This PR adds a `flake` function to haskell.nix projects. It can be used to transform the outputs of project into a flattened structure that can be used to make a `flake.nix` file for your project. Because the nix code and commands used are different a lot of stuff in the getting-started.md will not work with a `flake.nix`. So instead of trying to add a flake section to that guide this PR adds a new version for Nix Flake users.

commit172d402394cb7a85641d70e7cbdea9f37f071199 authorHamish Mackenzie <Hamish.Mackenzie@iohk.io> dateMon 22 Feb 2021, 20:42 (6 years ago) parentfd44431
3 files changed+177−4
docs/tutorials/getting-started-flakes.md+112 flake.nix+11−3 overlays/haskell.nix+54−1
docs/tutorials/getting-started-flakes.mdnew file+112
@@ -0,0 +1,112 @@
1+# Getting started with flakes
2+
3+This version of the getting started guide is for users who
4+are using [Nix Flakes](https://nixos.wiki/wiki/Flakes).
5+The non flakes version of the guide is [here](getting-started.md).
6+
7+`haskell.nix` can automatically translate your
8+[Cabal](https://cabal.readthedocs.io/en/latest/cabal-project.html)
9+or [Stack](https://docs.haskellstack.org/en/stable/README/#quick-start-guide)
10+project and its dependencies into Nix code.
11+
12+Assuming you have [Nix](https://nixos.org/download.html) installed, you can
13+start setting up your project.
14+
15+## Setting up the binary cache
16+
17+IMPORTANT: you *must* do this or you *will* build several copies of GHC!
18+
19+You can configure Nix to use our binary cache, which is pushed to by CI, so should contain the artifacts that you need.
20+
21+You need to add the following sections to `/etc/nix/nix.conf` or, if you are a trusted user, `~/.config/nix/nix.conf` (if you don't know what a "trusted user" is, you probably want to do the former).
22+
23+```
24+trusted-public-keys = [...] hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ= [...]
25+substituters = [...] https://hydra.iohk.io [...]
26+```
27+
28+This can be tricky to get setup properly. If you're still having trouble getting cache hits, consult the corresponding [troubleshooting section](../reference/troubleshooting#why-am-i-building-ghc).
29+
30+## Scaffolding
31+
32+The following work with `stack.yaml` and `cabal.project` based
33+projects.
34+
35+Add `flake.nix`:
36+
37+```nix
38+{
39+ description = "A very basic flake";
40+ inputs.haskellNix.url = "github:input-output-hk/haskell.nix";
41+ inputs.nixpkgs.follows = "haskellNix/nixpkgs-unstable";
42+ inputs.flake-utils.url = "github:numtide/flake-utils";
43+ outputs = { self, nixpkgs, flake-utils, haskellNix }:
44+ flake-utils.lib.eachSystem [ "x86_64-linux" "x86_64-darwin" ] (system:
45+ let
46+ overlays = [ haskellNix.overlay
47+ (final: prev: {
48+ # This overlay adds our project to pkgs
49+ helloProject =
50+ final.haskell-nix.project' {
51+ src = ./.;
52+ compiler-nix-name = "ghc8104";
53+ };
54+ })
55+ ];
56+ pkgs = import nixpkgs { inherit system overlays; };
57+ flake = pkgs.helloProject.flake {};
58+ in flake // {
59+ # Built by `nix build .`
60+ defaultPackage = flake.packages."hello:exe:hello";
61+
62+ # This is used by `nix develop .` to open a shell for use with
63+ # `cabal`, `hlint` and `haskell-language-server`
64+ devShell = pkgs.helloProject.shellFor {
65+ tools = {
66+ cabal = "latest";
67+ hlint = "latest";
68+ haskell-language-server = "latest";
69+ };
70+ };
71+ });
72+}
73+```
74+
75+!!! note "git dependencies"
76+ If you have git dependencies in your project, you'll need
77+ to [calculate sha256 hashes for them](./source-repository-hashes.md).
78+
79+### Working with a project
80+
81+Top-level attributes are Haskell packages (incl. dependencies) part of your project.
82+
83+To build the library component of a package in the project run:
84+
85+```shell
86+nix build .#your-package-name:lib:your-package-name
87+```
88+
89+There are also other components such as `exe`, `test` and `benchmark`.
90+To build an executable:
91+
92+```shell
93+nix build .#your-package-name:exe:your-exe-name
94+```
95+
96+To use the `devShell` provided by the flake run:
97+
98+```shell
99+nix develop .
100+cabal repl your-package-name:lib:your-package-name
101+cabal build your-package-name
102+```
103+
104+To open a shell for use with `stack` see [the following issue](https://github.com/input-output-hk/haskell.nix/issues/689#issuecomment-643832619).
105+
106+## Going forward
107+
108+Read through [project](../reference/library.md#project) function reference to see how the API works.
109+
110+There are a number of things to explore further in the tutorials section.
111+
112+[haskell.nix]: https://github.com/input-output-hk/haskell.nix
flake.nix+11−3
@@ -1,7 +1,15 @@
11{
22 description = "Alternative Haskell Infrastructure for Nixpkgs";
33
4− outputs = { self }:
4+ inputs = {
5+ # Note: keep this in sync with sources.json!
6+ nixpkgs.url = github:NixOS/nixpkgs/f02bf8ffb9a5ec5e8f6f66f1e5544fd2aa1a0693;
7+ nixpkgs-2003.url = github:NixOS/nixpkgs/7f73e46625f508a793700f5110b86f1a53341d6e;
8+ nixpkgs-2009.url = github:NixOS/nixpkgs/f02bf8ffb9a5ec5e8f6f66f1e5544fd2aa1a0693;
9+ nixpkgs-unstable.url = github:NixOS/nixpkgs/410bbd828cdc6156aecd5bc91772ad3a6b1099c7;
10+ };
11+
12+ outputs = { self, nixpkgs, ... }:
513 {
614
715 internal = rec {
@@ -27,7 +35,7 @@
2735 # `builtins.currentSystem` will not be supported in flakes.
2836 # https://github.com/NixOS/rfcs/pull/49/files#diff-a5a138ca225433534de8d260f225fe31R429
2937 overlay = self.overlays.combined-eval-on-build;
30− overlays = self.internal.overlaysOverrideable { sourcesOverride = self.sources; };
38+ overlays = self.internal.overlaysOverrideable { sourcesOverride = self.internal.sources; };
3139
3240 legacyPackages = let
3341 genAttrs = lst: f:
@@ -36,7 +44,7 @@
3644 value = f name;
3745 }) lst);
3846 in genAttrs [ "x86_64-linux" "x86_64-darwin" ] (system:
39− import self.internal.sources.nixpkgs
47+ import nixpkgs
4048 (self.internal.nixpkgsArgs // { localSystem = { inherit system; }; }));
4149 };
4250}
overlays/haskell.nix+54−1
@@ -560,7 +560,7 @@ final: prev: {
560560 }
561561 ) (builtins.removeAttrs rawProject.hsPkgs
562562 # These are functions not packages
563− [ "shellFor" "makeConfigFiles" "ghcWithHoogle" "ghcWithPackages" ]);
563+ [ "shellFor" "makeConfigFiles" "ghcWithHoogle" "ghcWithPackages" "buildPackages" ]);
564564
565565 projectCoverageReport = haskellLib.projectCoverageReport project (map (pkg: pkg.coverageReport) (final.lib.attrValues (haskellLib.selectProjectPackages hsPkgs)));
566566
@@ -568,6 +568,59 @@ final: prev: {
568568 rawProject.projectFunction pkgs.haskell-nix rawProject.projectArgs
569569 ) final.pkgsCross) // { recurseForDerivations = false; };
570570
571+ # Helper function that can be used to make a Nix Flake out of a project
572+ # by including a flake.nix. See docs/tutorials/getting-started-flakes.md
573+ # for an example flake.nix file.
574+ # This flake function maps the build outputs to the flake `packages`,
575+ # `checks` and `apps` output attributes.
576+ flake = {
577+ packages ? haskellLib.selectProjectPackages
578+ }:
579+ let packageNames = builtins.attrNames (packages project.hsPkgs);
580+ in {
581+ # Used by:
582+ # `nix build .#pkg-name:lib:pkg-name`
583+ # `nix build .#pkg-name:lib:sublib-name`
584+ # `nix build .#pkg-name:exe:exe-name`
585+ # `nix build .#pkg-name:test:test-name`
586+ packages = builtins.listToAttrs (
587+ final.lib.concatMap (packageName:
588+ let package = project.hsPkgs.${packageName};
589+ in final.lib.optional (package.components ? library)
590+ { name = "${packageName}:lib:${packageName}"; value = package.components.library; }
591+ ++ final.lib.mapAttrsToList (n: v:
592+ { name = "${packageName}:lib:${n}"; value = v; })
593+ (package.components.sublibs)
594+ ++ final.lib.mapAttrsToList (n: v:
595+ { name = "${packageName}:exe:${n}"; value = v; })
596+ (package.components.exes)
597+ ++ final.lib.mapAttrsToList (n: v:
598+ { name = "${packageName}:test:${n}"; value = v; })
599+ (package.components.tests)
600+ ) packageNames);
601+ # Used by:
602+ # `nix check .#pkg-name:test:test-name`
603+ checks = builtins.listToAttrs (
604+ final.lib.concatMap (packageName:
605+ let package = project.hsPkgs.${packageName};
606+ in final.lib.mapAttrsToList (n: v:
607+ { name = "${packageName}:test:${n}"; value = v; })
608+ (final.lib.filterAttrs (_: v: final.lib.isDerivation v) (package.checks))
609+ ) packageNames);
610+ # Used by:
611+ # `nix run .#pkg-name:exe:exe-name`
612+ # `nix run .#pkg-name:test:test-name`
613+ apps = builtins.listToAttrs (
614+ final.lib.concatMap (packageName:
615+ let package = project.hsPkgs.${packageName};
616+ in final.lib.mapAttrsToList (n: v:
617+ { name = "${packageName}:exe:${n}"; value = { type = "app"; program = v.exePath; }; })
618+ (package.components.exes)
619+ ++ final.lib.mapAttrsToList (n: v:
620+ { name = "${packageName}:test:${n}"; value = { type = "app"; program = v.exePath; }; })
621+ (package.components.tests)
622+ ) packageNames);
623+ };
571624 inherit (rawProject.hsPkgs) makeConfigFiles ghcWithHoogle ghcWithPackages shellFor;
572625 });
573626