Skip to content
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

Documentation - Working with Nix #79

Open
shmish111 opened this issue Nov 9, 2018 · 4 comments
Open

Documentation - Working with Nix #79

shmish111 opened this issue Nov 9, 2018 · 4 comments

Comments

@shmish111
Copy link

I spent a couple of days trying to get Hint working on Nix and I've finally managed it with the help of @mchakravarty. Below is what was required, could you document this somewhere obvious, maybe in the README.md?

Using hint with nix

Hint relies on certain GHC paths being set up correctly to function, these are the path to the library of packages and the path to the package database. If you are running hint with stack exec then this works without any modification. This is because stack sets the env var GHC_PACKAGE_PATH as well as GHC libraries being in the expected places. If you run the binary produced by stack build by itself you will need to set GHC_PACKAGE_PATH to the value you can find with stack exec -- env | grep GHC_PACKAGE_PATH.

Unfortunately when you build your project with nix you will find that although your program runs, the hint interpreter can't find the package library files or the package db. There are two things you must do to solve these problems.

Set GHC_PACKAGE_PATH

You will need to build a ghc binary that has all the packages that hint will need when interpreting code:

  myGhc = pkgs.haskell.packages.ghc822.ghcWithPackages (ps: [
    myPackageOne
    myPackageTwo
  ]);

You will then need to wrap up your binary in a shell script that will set the env var:

  my-program-invoker = pkgs.stdenv.mkDerivation {
      name = "my-program-invoker";
      unpackPhase = "true";
      buildInputs = [ myGhc my-program pkgs.makeWrapper ];
      buildPhase = ''
        # We need to provide the ghc interpreter (hint) with the location of the ghc lib dir and the package db
        mkdir $out
        ln -s ${my-program}/bin/my-program $out/my-program
        # Unfortunately ghc-8.2.2 is hardcoded here so when you want to upgrade GHC you will need to fix this
        wrapProgram $out/my-program --set GHC_PACKAGE_PATH "${myGhc}/lib/ghc-8.2.2/package.conf.d"
        '';
      installPhase = "echo nothing to install";
  };

Now hint will be able to find the package database, however it still can't find the library files and you will probably get an error can't find Prelude.

Set library directory

Hint provides unsafeRunInterpreterWithArgsLibdir to do this. You will need to set an env var (or pass in on the cli prompt) with the location of myGhc library directory. In order to allow stack to work as well as nix, I put in a bit of boiler plate:

runInterpreter :: InterpreterT IO a -> IO (Either InterpreterError a)
runInterpreter action = do
  mLibDir <- liftIO $ lookupEnv "GHC_LIB_DIR"
  case mLibDir of
    Just libDir ->
      unsafeRunInterpreterWithArgsLibdir [] libDir action
    Nothing -> Interpreter.runInterpreter action

I then need to set GHC_LIB_DIR in the nix wrapper script by adding an extra --set flag, the final invoker wrapper will look like this:

  my-program-invoker = pkgs.stdenv.mkDerivation {
      name = "my-program-invoker";
      unpackPhase = "true";
      buildInputs = [ myGhc my-program pkgs.makeWrapper ];
      buildPhase = ''
        # We need to provide the ghc interpreter (hint) with the location of the ghc lib dir and the package db
        mkdir $out
        ln -s ${my-program}/bin/my-program $out/my-program
        # Unfortunately ghc-8.2.2 is hardcoded here so when you want to upgrade GHC you will need to fix this
        wrapProgram $out/my-program --set GHC_LIB_DIR "${myGhc}/lib/ghc-8.2.2" --set GHC_PACKAGE_PATH "${myGhc}/lib/ghc-8.2.2/package.conf.d"
        '';
      installPhase = "echo nothing to install";
  };

After doing this, nix will build a GHC with all the correct libraries and a wrapper script which you can then use to run your program and hint should work correctly.

@gelisam
Copy link
Contributor

gelisam commented Dec 26, 2018

You will need to build a ghc binary that has all the packages

That sounds crazy heavyweight, is this commonly done in the nix world? GHC_PACKAGE_PATH is a colon-separated list of paths, wouldn't it be simpler to compile your dependencies to their own package database and to add the path of that package database to GHC_PACKAGE_PATH, instead of adding them to ghc's package database?

@shmish111
Copy link
Author

@gelisam when I said a "ghc binary" what I really meant was a ghc environment, basically ghc and a package database with everything in the correct place etc, most things are symlinks to the nix store anyway.

@anka-213
Copy link

anka-213 commented Jul 10, 2020

@shmish111 We can fix the hard-coded ghc version by extracting it from the myGhc derivation:

  my-program-invoker = pkgs.stdenv.mkDerivation {
      name = "my-program-invoker";
      unpackPhase = "true";
      buildInputs = [ myGhc my-program pkgs.makeWrapper ];
      buildPhase = ''
        # We need to provide the ghc interpreter (hint) with the location of the ghc lib dir and the package db
        mkdir -p $out/bin
        ln -s ${my-program}/bin/my-program $out/bin/my-program
        # Fortunately ${myGhc.meta.name} is no longer hardcoded here so you don't need to fix anything to upgrade GHC
        wrapProgram $out/bin/my-program --set GHC_PACKAGE_PATH "${myGhc}/lib/${myGhc.meta.name}/package.conf.d"
        '';
      installPhase = "echo nothing to install";
  };

I also added bin/ to the path of the wrapper, so it will be included in PATH properly.

@domenkozar
Copy link

For haskell.nix users: https://github.com/input-output-hk/plutus/blob/fb7ccb60fe58d14fe4f1bfadcdc87a5aaeca3d1d/marlowe-playground-client/default.nix#L13-L22

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants