Automatic stack nix integration without the `--nix` flag
CMCDragonkai opened this issue · 6 comments
The usage of the --nix
flag is a bad idea because it only works at the shell and is not inherited by anything in the shell that utilises stack
.
At the same time we don't want to have a config parameter that enables nix
because that means the stack.yaml
requires to be changed when working on a non-nix environment.
I'm tracking an issue about automatic nix integration: commercialhaskell/stack#3980
But it's not working even at 1.7.1 and the PR implies that it only works when on NixOS, not that when inside a nix environment.
An alternative way is use makeWrapper
and buildEnv
in nix to wrap the stack
command such that it always uses stack --nix
.
{
pkgs ? import (fetchTarball https://github.com/NixOS/nixpkgs-channels/archive/8b1cf100cd8badad6e1b6d4650b904b88aa870db.tar.gz) {}
}:
with pkgs;
let
stack-nix = buildEnv {
name = "stack-nix";
paths = [ stack ];
pathsToLink = [ "/" "/bin" ];
buildInputs = [ makeWrapper ];
postBuild = ''
wrapProgram $out/bin/stack --add-flags '--nix'
'';
version = stack.version;
};
in
haskell.lib.buildStackProject {
name = "stack-test";
buildInputs = [ stack-nix ];
}
It ends up working, but only because the buildInputs
includes stack-nix
ahead of its own stack
. So it's stack
command takes precedence in the shell's PATH
environment variable. Ideally we would override the stack
argument used in buildStackProject
. However it cannot be done with buildEnv
, it would have to be a the original derivation with its build script overridden or added an additional stage that wraps the stack command.
(haskell.lib.buildStackProject.override { stack = stack-nix; }) {
name = "stack-test";
}
Attempting this variation just to see if it can work:
{
pkgs ? import (fetchTarball https://github.com/NixOS/nixpkgs-channels/archive/be1461fc0ab29c45c69e7b2c2097c887750e4fe0.tar.gz) {}
}:
with pkgs;
let
stack-nix = stack.overrideAttrs (oldAttrs: {
buildInputs = oldAttrs.buildInputs ++ [ makeWrapper ];
postPhases = (pkgs.lib.attrByPath [ "postPhases" ] [] oldAttrs) ++ [ "stackNixWrapPhase" ];
stackNixWrapPhase = ''
wrapProgram $out/bin/stack --add-flags '--nix'
'';
});
# stack-nix = stack;
in
(haskell.lib.buildStackProject.override { stack = stack-nix; }) {
name = "stack-test";
buildInputs = [];
}
If it does, there's still a downside, it requires recompilation of stack. Which is quite expensive compared to just using buildEnv
and just ensuring that stack-nix
comes ahead of the original stack
.
The above build completes, and simple stack --version
and stack init
works, but then stack ghci
and stack build
freezes. So I'm guessing the buildEnv
is just the superior solution.
Actually waiting for a while results in this report:
/nix/store/8zkg9ac4s4alzyf4a8kfrig1j73z66dw-bash-4.4-p23/bin/bash: warning: shell level (1000) too high, resetting to 1
Which I suspected there was an infinite loop being caused by the makeWrapper
that is taking over the very path of stack
.
It turns out that both the buildEnv
solution and the overrideAttrs
solutions both don't work when using stack ghci
or stack build
, this is due to some sort of infinite loop. Maybe because the redirection method of wrapProgram
doesn't work with the way stack invokes itself.
The only safest solution right now to either wait for stack to implement automatic nix integration or use nix integration at the stack global configuration: https://github.com/commercialhaskell/stack/blob/master/doc/yaml_configuration.md#non-project-specific-config
That's it.
Just add:
nix:
enable: true
Into your ~/.stack/config.yaml
, then you'll get automatic nix integration. No longer need to use stack --nix
. Note that switches it on for the entire user profile which is unfortunate, but avoids us from forcing nix integration using the project specific stack.yaml
and we can use stack inside a nix-shell.
Note that stack tutorials think that the user wants to use nix inside stack, but we are actually using stack inside nix, not the same thing. But the --nix
integration is still needed for stack to utilise nix installed GHC.
However note that means ALL stack projects will have nix enabled. So that may not be a good idea when working on stack projects (like third party projects) that don't have Nix enabled. I suspect that stack will retry gracefully without nix though.
No longer relevant when not using stack.