Skip to content

Add flake support to hix #1572

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 4, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
228 changes: 9 additions & 219 deletions docs/tutorials/getting-started-hix.md
Original file line number Diff line number Diff line change
@@ -1,227 +1,17 @@
# Getting started with Hix

The `hix` tools are wrappers for the various `nix` tools that
use `haskell.nix` without the need to add any `.nix` files.
Hix is a command line tool that provides an easy way to add haskell.nix
support to existing haskell projects.

This is useful for:
The `hix init` command adds a `flake.nix` and `nix/hix.nix` file.
After that the project can be used with regular `nix` tools.

* A quick way to try out haskell.nix for new users.
For instance to run `cabal build` on the `hello` package from hackage:

* Using haskell.nix to work on projects that do not have
`.nix` files.

* Testing to see if `haskell.nix` can build a project.

* Making `flake` and `non flake` configurations to check `haskell.nix`
treats them the same.

## Installing Nix
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can add a small n.b. to remind user that hix require having nix in PATH with experimental-features = [ "nix-command" "flakes" ]; enabled?


To use Hix you will need to install [Nix](https://nixos.org/download.html).

## Setting up the binary cache

IMPORTANT: you *must* do this or you *will* build several copies of GHC!

You can configure Nix to use our binary cache, which is pushed to by CI, so should contain the artifacts that you need.

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).

```
trusted-public-keys = [...] hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ= [...]
substituters = [...] https://cache.iog.io [...]
```

If you're running NixOS, you need to add/update the following in your `/etc/nixos/configuration.nix` files instead.

```
# Binary Cache for Haskell.nix
nix.settings.trusted-public-keys = [
"hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ="
];
nix.settings.substituters = [
"https://cache.iog.io"
];
```

NixOS-21.11 and older use slightly different settings.

```
# Binary Cache for Haskell.nix
nix.binaryCachePublicKeys = [
"hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ="
];
nix.binaryCaches = [
"https://cache.iog.io"
];
```

This can be tricky to get setup properly. If you're still having trouble getting cache hits, consult the corresponding [troubleshooting section](../troubleshooting.md#why-am-i-building-ghc).

## Installing Hix

```
nix-env -iA hix -f https://github.com/input-output-hk/haskell.nix/tarball/master
```

## Updating Hix (also updates hackage)

```
hix update
```

This is also necessary to make the latest nightly snapshot of hackage
avaiable to nix.

## Building with Hix

To run `cabal build` in a nix-shell with all the dependencies required:

```
```bash
cabal unpack hello
cd hello-1.0.0.2
hix-shell --run 'cabal build'
```

Build with nix:

```
hix-build -A hsPkgs.hello.components.exes.hello
```

Cross compile to JavaScript:

```
hix-build -A projectCross.ghcjs.hsPkgs.hello.components.exes.hello
```

## Configuring Hix

The configuration arguments for `Hix` can be (from highest precedence to lowest):

* Passed on the command line with `--arg` (or `--argstr` for string args).

* Placed in `nix/hix.nix` file in the project dir.

* Placed in `~/.config/hix/hix.conf`

For example to build with GHC 8.10.7:

```
hix-shell --argstr compiler-nix-name ghc8107 --run 'cabal build'
```

or add a `nix/hix.nix` or `~/.config/hix/hix.conf` file:

```nix
{ compiler-nix-name = "ghc8107"; }
```

Here are just a few of the other configuration arguments you could use
in the files or on the command line (they are all optional):

```nix
{ name = "hello"; # for better error messages and derivation names
nixpkgsPin = "nixpkgs-unstable"; # or nixpkgs-2111 or nixpkgs-2105
nixpkgs = <nixpkgs>; # use this instead of nixpkgsPin
subDir = "some/sub/dir"; # sub dir containing the haskell project
projectFileName = "stack.yaml"; # use this project file
tools.haskell-language-server = "latest";
tools.hlint = "latest"; # Include the latest hls and hlint in the shell
index-state = "2021-02-22T00:00:00Z"; # It is normally best to put this in `cabal.project` (not here)

# PLUS MANY MORE! Almost any argument you can pass to the project functions
# or to `shellFor` can be used in as a Hix configuration argument.

}
```

## Adding Nix Support with Niv

If you have a `nix/hix.nix` file with suitable configuration that
you want to make available to users with Nix (without having to
install Hix).

[Niv](https://github.com/nmattia/niv) is a command line tool for keeping track of Nix project dependencies.

After installing niv you can initialize niv and pin the latest haskell.nix
commit by running the following in the root directory of the project:

```
niv init
niv add input-output-hk/haskell.nix -n haskellNix
nix run github:input-output-hk/haskell.nix#hix -- init
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested it using:

nix run "github:input-output-hk/haskell.nix/hkm/hix-flake#hix" -- init

zsh will complain if nix run argument not enclosed in quotes:

zsh: no matches found: github:input-output-hk/haskell.nix/hkm/hix-flake#hix

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just discover that's not default zsh who complain when argument contains : not enclosed in quotes, that's the GRML config I'm using …

nix develop
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All nix commands fail on my computer: error: getting status of '/nix/store/m5kk00h3yvjbyw4frcf00hzaigf0p7p0-source/hello-1.0.0.2': No such file or directory

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, hello-1.0.0.2 folder was in another flake subdirectory and I guess direnv mess a bit with dev shells … now I have this issue:

yvan@X230 ~/IOHK/hello-1.0.0.2 % nix develop
warning: Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json.
warning: Using saved setting for 'extra-substituters = https://cache.iog.io' from ~/.local/share/nix/trusted-settings.json.
warning: Using saved setting for 'extra-trusted-public-keys = hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=' from ~/.local/share/nix/trusted-settings.json.
error: The option `crossPlatforms' does not exist. Definition values:
       - In `<unknown-file>': <function>
(use '--show-trace' to show detailed location information)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Full log with --show-trace:

error: The option `crossPlatforms' does not exist. Definition values:
       - In `<unknown-file>': <function>

       … while evaluating the attribute 'config'

       at /nix/store/l4ysisamwr0jdf6w2za4zygj9ajlf0wb-source/lib/modules.nix:357:9:

          356|         options = checked options;
          357|         config = checked (removeAttrs config [ "_module" ]);
             |         ^
          358|         _module = checked (config._module);

       … while evaluating 'hasSuffix'

       at /nix/store/l4ysisamwr0jdf6w2za4zygj9ajlf0wb-source/lib/strings.nix:235:5:

          234|     # Input string
          235|     content:
             |     ^
          236|     let

       … from call site

       at /nix/store/im0h1rigra4mw84y1ml8k2mr6ryjrpdc-source/overlays/haskell.nix:839:16:

          838|           in
          839|             if final.lib.hasSuffix ".yaml" selectedFileName
             |                ^
          840|               then stackProject' ([

       … while evaluating 'project''

       at /nix/store/im0h1rigra4mw84y1ml8k2mr6ryjrpdc-source/overlays/haskell.nix:811:20:

          810|         # used (as it will use a default `cabal.project`).
          811|         project' = projectModule:
             |                    ^
          812|           let

       … from call site

       at /nix/store/im0h1rigra4mw84y1ml8k2mr6ryjrpdc-source/overlays/haskell.nix:852:33:

          851|         # for `cabalPackage` and `stackPackage`.
          852|         project = args: let p = project' args;
             |                                 ^
          853|           in p.hsPkgs // p;

       … while evaluating 'project'

       at /nix/store/im0h1rigra4mw84y1ml8k2mr6ryjrpdc-source/overlays/haskell.nix:852:19:

          851|         # for `cabalPackage` and `stackPackage`.
          852|         project = args: let p = project' args;
             |                   ^
          853|           in p.hsPkgs // p;

       … from call site

       at /nix/store/im0h1rigra4mw84y1ml8k2mr6ryjrpdc-source/overlays/hix.nix:38:8:

           37|       projectDefaults = importDefaults (toString (src.origSrcSubDir or src) + "/nix/hix.nix");
           38|     in final.haskell-nix.project [
             |        ^
           39|             (import ../modules/hix-project.nix)

       … while evaluating 'project'

       at /nix/store/im0h1rigra4mw84y1ml8k2mr6ryjrpdc-source/overlays/hix.nix:3:5:

            2|   project =
            3|     { src
             |     ^
            4|     , userDefaults ? {}

       … from call site

       at /nix/store/07ym39kzdnn7h45zz6v78286d3vs1sax-source/flake.nix:19:15:

           18|             hixProject =
           19|               final.haskell-nix.hix.project {
             |               ^
           20|                 src = ./.;

       … while evaluating the attribute 'hixProject.flake'

       at /nix/store/07ym39kzdnn7h45zz6v78286d3vs1sax-source/flake.nix:18:13:

           17|           (final: prev: {
           18|             hixProject =
             |             ^
           19|               final.haskell-nix.hix.project {

       … while evaluating anonymous lambda

       at /nix/store/07ym39kzdnn7h45zz6v78286d3vs1sax-source/flake.nix:14:52:

           13|     in
           14|       flake-utils.lib.eachSystem supportedSystems (system:
             |                                                    ^
           15|       let

       … from call site

       at /nix/store/0zdncmv2n5sncs0sdphvggc61ix1fpsm-source/default.nix:128:17:

          127|         let
          128|           ret = f system;
             |                 ^
          129|           op = attrs: key:

       … while evaluating 'op'

       at /nix/store/0zdncmv2n5sncs0sdphvggc61ix1fpsm-source/default.nix:126:19:

          125|       # Merge together the outputs for all systems.
          126|       op = attrs: system:
             |                   ^
          127|         let

       … from call site

       at /nix/store/0zdncmv2n5sncs0sdphvggc61ix1fpsm-source/default.nix:144:5:

          143|     in
          144|     builtins.foldl' op { } systems
             |     ^
          145|   ;

       … while evaluating 'eachSystem'

       at /nix/store/0zdncmv2n5sncs0sdphvggc61ix1fpsm-source/default.nix:98:25:

           97|   #
           98|   eachSystem = systems: f:
             |                         ^
           99|     let

       … from call site

       at /nix/store/07ym39kzdnn7h45zz6v78286d3vs1sax-source/flake.nix:14:7:

           13|     in
           14|       flake-utils.lib.eachSystem supportedSystems (system:
             |       ^
           15|       let

       … while evaluating 'outputs'

       at /nix/store/07ym39kzdnn7h45zz6v78286d3vs1sax-source/flake.nix:5:13:

            4|   inputs.flake-utils.url = "github:numtide/flake-utils";
            5|   outputs = { self, nixpkgs, flake-utils, haskellNix }:
             |             ^
            6|     let

       … from call site

       at «string»:45:21:

           44|
           45|           outputs = flake.outputs (inputs // { self = result; });
             |                     ^
           46|

       … while evaluating anonymous lambda

       at «string»:10:13:

            9|     builtins.mapAttrs
           10|       (key: node:
             |             ^
           11|         let

       … from call site

       … while evaluating anonymous lambda

       at «string»:2:23:

            1|
            2| lockFileStr: rootSrc: rootSubdir:
             |                       ^
            3|

       … from call site

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot to say when testing this you need to also pass something to point haskellNix to the PR branch. Something like:

nix develop --override-input haskellNix "github:input-output-hk/haskell.nix/hkm/hix-flake"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I confirm that everything works as expected :)

cabal build
```

Add `default.nix`:

```nix
(import (import nix/sources.nix).haskellNix {}).hix.project { src = ./.; }
```

If you want to also pin `nixpkgs` with Niv use:

```nix
let
sources = import nix/sources.nix;
in
(import sources.haskellNix {}).hix.project {
inherit (sources) nixpkgs;
src = ./.;
}
```

Add `shell.nix`:

```nix
(import ./.).shell
```

When you want to update to the latest version of haskell.nix use:

```
niv update haskellNix
```

## Adding Nix Flake Support

To add flake support that uses the `nix/hix.nix` configuration in your
follow the [Getting started with flakes](getting-started.md) guide, but
use `haskell-nix.hix.project` instead of `haskell-nix.project'`

The `nixpkgs` used will need to be selected as a flake input (any selection
made in `nix/hix.nix` will be ignored).

Example `flake.nix` file:

```nix
{
description = "A very basic flake";
inputs.haskellNix.url = "github:input-output-hk/haskell.nix";
inputs.nixpkgs.follows = "haskellNix/nixpkgs-unstable";
inputs.flake-utils.url = "github:numtide/flake-utils";
outputs = { self, nixpkgs, flake-utils, haskellNix }:
flake-utils.lib.eachSystem [ "x86_64-linux" "x86_64-darwin" ] (system:
let
overlays = [ haskellNix.overlay
(final: prev: {
# This overlay adds our project to pkgs
helloProject =
final.haskell-nix.hix.project {
src = ./.;
# Other project options can be put in `nix/hix.nix`
};
})
];
pkgs = import nixpkgs { inherit system overlays; inherit (haskellNix) config; };
flake = pkgs.helloProject.flake {
# This adds support for `nix build .#js-unknown-ghcjs:hello:exe:hello`
crossPlatforms = p: [p.ghcjs];
};
in flake // {
# Built by `nix build .`
defaultPackage = flake.packages."hello:exe:hello";
});
}
```


2 changes: 2 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@
# Exposed so that buildkite can check that `allow-import-from-derivation=false` works for core of haskell.nix
roots = legacyPackagesUnstable.haskell-nix.roots compiler;

packages = ((self.internal.compat { inherit system; }).hix).apps;

devShell = with self.legacyPackages.${system};
mkShell {
buildInputs = [
Expand Down
Loading