Getting envvars into Haskell builds - Nix

Welcome to the Functional Programming Zulip Chat Archive. You can join the chat here.

Magnus Therning

When building on TravisCI we pull in the values of a few of Travis envvars (https://docs.travis-ci.com/user/environment-variables/#default-environment-variables). It seems they aren't pushed into the nix-build environment. Any pointers on how to do that would be most appreciated.

Environment Variables
Vaibhav Sagar

By default Nix doesn't have dependencies on your existing environment without them being explicitly specified. If you want to do this, I would suggest adding a parameter to the .nix file and then passing them in when you build using --arg

Magnus Therning

How can I explicitly specify that an envvar should be passed through?

I don't succeed in getting anything I pass in via --arg to actually end up as an envvar in the build environment.

Vaibhav Sagar

Here's an example:

default.nix

{ environmentVariable ? null }: let
  pkgs = import <nixpkgs> {};
in pkgs.runCommand "example" {
  inherit environmentVariable;
}
''
  echo $environmentVariable
  touch $out
''

and you can use it as follows:

$ nix-build
these derivations will be built:
  /nix/store/mcwn62zwczay4a06mk12zrs77424zgrj-example.drv
building '/nix/store/mcwn62zwczay4a06mk12zrs77424zgrj-example.drv'...

/nix/store/pa9p2zzmdgp8bgxw6qz42kdbrxdm9vr1-example

$ nix-build --argstr environmentVariable "hello!"
these derivations will be built:
  /nix/store/2d5yzznb8wg721lblrcv5pv8wrxy2871-example.drv
building '/nix/store/2d5yzznb8wg721lblrcv5pv8wrxy2871-example.drv'...
hello!
/nix/store/11gn4sppncwzqp9dnrbk9snvisvy3xlf-example
Magnus Therning

Yes, that's how I can get an envvar into a runCommand, which takes envvars as an argument. I need the envvar to end up in a haskellPackage.developPackage which doesn't take envvars as an argument :thinking:

Vaibhav Sagar

What is using these environment variables?

Magnus Therning

They are used during the build itself. Template Haskell picking them up and embedding them in the binary.

Vaibhav Sagar

You can use preBuild to set those environment variables:

default.nix

{ environmentVariable ? "" }:
let
  pkgs = import <nixpkgs> {};
  modifier = drv: pkgs.haskell.lib.overrideCabal drv (old: {
    preBuild = ''
      export environmentVariable=${environmentVariable}
    '';
  });
in pkgs.haskellPackages.developPackage {
  inherit modifier;
  root = ./.;
}

Main.hs

{-# LANGUAGE TemplateHaskell #-}

module Main where

import Language.Haskell.TH
import System.Environment (lookupEnv)
import Data.Maybe (fromMaybe)

value :: String
value = $(runIO $ do
  var <- lookupEnv "environmentVariable"
  pure $ LitE $ StringL $ fromMaybe "" var)

main :: IO ()
main = putStrLn value

I also needed a .cabal file but I haven't included it here. This is how you would use it:

$ nix-build --argstr environmentVariable "hello!"
<...>
$ result/bin/environment-example
hello!
Magnus Therning

I can only have one preBuild right?

Vaibhav Sagar

No, you can have multiple ones as long as you prepend previous overrides by doing something like

preBuild = (old.preBuild or "") ++ ''
  <whatever>
''
Magnus Therning

Apparently the following works too:

modifier = drv: drv.overrideAttrs (old: old // {
    envvar = 17;
  });
Magnus Therning

Thanks for your help. You put me on track to that overrideAttrs solution, which I rather like (easier to compose).